From bee4902323d79a7b3dd2460fdf3092e06bb33e6c Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 20 Aug 2017 11:47:45 -0400 Subject: [PATCH 01/45] add Sayma AMC standalone target --- .../gateware/targets/sayma_amc_standalone.py | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100755 artiq/gateware/targets/sayma_amc_standalone.py diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py new file mode 100755 index 000000000..5370b69a6 --- /dev/null +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 + +import argparse + +from migen import * + +from misoc.cores import gpio +from misoc.integration.soc_sdram import soc_sdram_args, soc_sdram_argdict +from misoc.integration.builder import builder_args, builder_argdict +from misoc.targets.sayma_amc import MiniSoC + +from artiq.gateware.amp import AMPSoC, build_artiq_soc +from artiq.gateware import rtio +from artiq.gateware.rtio.phy import ttl_simple +from artiq import __version__ as artiq_version + + +class SaymaAMCStandalone(MiniSoC, AMPSoC): + mem_map = { + "cri_con": 0x10000000, + "rtio": 0x20000000, + "rtio_dma": 0x30000000, + "mailbox": 0x70000000 + } + mem_map.update(MiniSoC.mem_map) + + def __init__(self, cpu_type="or1k", **kwargs): + MiniSoC.__init__(self, + cpu_type=cpu_type, + sdram_controller_type="minicon", + l2_size=128*1024, + ident=artiq_version, + ethmac_nrxslots=4, + ethmac_ntxslots=4, + **kwargs) + AMPSoC.__init__(self) + platform = self.platform + platform.toolchain.bitstream_commands.extend([ + "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", + ]) + + self.submodules.leds = gpio.GPIOOut(Cat( + platform.request("user_led", 0), + platform.request("user_led", 1))) + self.csr_devices.append("leds") + + rtio_channels = [] + for i in (2, 3): + phy = ttl_simple.Output(platform.request("user_led", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + + for i in (0, 1): + sma_io = platform.request("sma_io", i) + self.comb += sma_io.direction.eq(1) + phy = ttl_simple.Output(sma_io.level) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + + self.config["HAS_RTIO_LOG"] = None + self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels) + rtio_channels.append(rtio.LogChannel()) + + self.clock_domains.cd_rtio = ClockDomain() + self.comb += [ + self.cd_rtio.clk.eq(ClockSignal()), + self.cd_rtio.rst.eq(ResetSignal()) + ] + self.submodules.rtio_core = rtio.Core(rtio_channels) + self.csr_devices.append("rtio_core") + self.submodules.rtio = rtio.KernelInitiator() + self.submodules.rtio_dma = ClockDomainsRenamer("sys_kernel")( + rtio.DMA(self.get_native_sdram_if())) + self.register_kernel_cpu_csrdevice("rtio") + self.register_kernel_cpu_csrdevice("rtio_dma") + self.submodules.cri_con = rtio.CRIInterconnectShared( + [self.rtio.cri, self.rtio_dma.cri], + [self.rtio_core.cri]) + self.register_kernel_cpu_csrdevice("cri_con") + self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) + self.csr_devices.append("rtio_moninj") + + self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_core.cri, + self.get_native_sdram_if()) + self.csr_devices.append("rtio_analyzer") + +def main(): + parser = argparse.ArgumentParser( + description="ARTIQ device binary builder / Sayma AMC stand-alone") + builder_args(parser) + soc_sdram_args(parser) + args = parser.parse_args() + + soc = SaymaAMCStandalone(**soc_sdram_argdict(args)) + build_artiq_soc(soc, builder_argdict(args)) + + +if __name__ == "__main__": + main() From 9f4c9fc14b22df8534ff9f9836ce6f63465be7f3 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 20 Aug 2017 17:23:56 -0400 Subject: [PATCH 02/45] artiq_flash: Sayma support --- artiq/frontend/artiq_flash.py | 239 +++++++++++++++++++++++++--------- 1 file changed, 177 insertions(+), 62 deletions(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index 819c2c8a1..daf48d27c 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# Copyright (C) 2015 Robert Jordens import argparse import os @@ -11,16 +10,6 @@ from artiq import __artiq_dir__ as artiq_dir from artiq.frontend.bit2bin import bit2bin -def scripts_path(): - p = ["share", "openocd", "scripts"] - if os.name == "nt": - p.insert(0, "Library") - p = os.path.abspath(os.path.join( - os.path.dirname(shutil.which("openocd")), - "..", *p)) - return p - - def get_argparser(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, @@ -47,8 +36,8 @@ Prerequisites: """) parser.add_argument("-t", "--target", default="kc705", help="target board, default: %(default)s") - parser.add_argument("-m", "--adapter", default="nist_clock", - help="target adapter, default: %(default)s") + parser.add_argument("-m", "--adapter", default=None, + help="target adapter, default: board-dependent") parser.add_argument("--target-file", default=None, help="use alternative OpenOCD target file") parser.add_argument("-f", "--storage", help="write file to storage area") @@ -59,84 +48,210 @@ Prerequisites: return parser +def scripts_path(): + p = ["share", "openocd", "scripts"] + if os.name == "nt": + p.insert(0, "Library") + p = os.path.abspath(os.path.join( + os.path.dirname(shutil.which("openocd")), + "..", *p)) + return p + + +class Programmer: + def __init__(self, target_file): + self.target_file = target_file + self.prog = [] + + def load(self, bitfile): + raise NotImplementedError + + def proxy(self, proxy_bitfile): + raise NotImplementedError + + def flash_binary(self, flashno, address, filename): + raise NotImplementedError + + def start(self): + raise NotImplementedError + + def do(self): + self.prog.append("exit") + cmdline = [ + "openocd", + "-s", scripts_path() + ] + if self.target_file is not None: + cmdline += ["-f", target_file] + cmdline += ["-c", "; ".join(self.prog)] + subprocess.check_call(cmdline) + + +class ProgrammerJtagSpi7(Programmer): + def __init__(self, target_file): + Programmer.__init__(self, target_file) + self.prog.append("init") + + def load(self, bitfile): + self.prog.append("pld load 0 " + bitfile) + + def proxy(self, proxy_bitfile): + self.prog.append("jtagspi_init 0 {{{}}}".format(proxy_bitfile)) + + def flash_binary(self, flashno, address, filename): + # jtagspi_program supports only one flash + assert flashno == 0 + self.prog.append("jtagspi_program {{{}}} 0x{:x}".format( + filename, address)) + + def start(self): + self.prog.append("xc7_program xc7.tap") + + +class ProgrammerSayma(Programmer): + def __init__(self, target_file): + # TODO: use target_file + # TODO: support Sayma RTM + Programmer.__init__(self, None) + self.proxy_loaded = False + self.prog += [ + "interface ftdi", + "ftdi_device_desc \"Quad RS232-HS\"", + "ftdi_vid_pid 0x0403 0x6011", + "ftdi_channel 0", + # EN_USB_JTAG on ADBUS7: out, high + # nTRST on ADBUS4: out, high, but R46 is DNP + "ftdi_layout_init 0x0098 0x008b", + "ftdi_tdo_sample_edge falling", + "ftdi_layout_signal nSRST -data 0x0080", + "reset_config srst_only srst_pulls_trst srst_gates_jtag srst_push_pull", + + "adapter_khz 25000", + + "transport select jtag", + + "jtag newtap amc_xcu tap -irlen 6 -ignore-version -expected-id 0x03822093", + + "pld device virtex2 amc_xcu.tap 1", + + "set XILINX_USER1 0x02", + "set XILINX_USER2 0x03", + "set AMC_DR_LEN 2", + + "target create amc_xcu.proxy testee -chain-position amc_xcu.tap", + "flash bank amc_xcu.spi0 jtagspi 0 0 0 0 amc_xcu.proxy $XILINX_USER1 $AMC_DR_LEN", + "flash bank amc_xcu.spi1 jtagspi 0 0 0 0 amc_xcu.proxy $XILINX_USER2 $AMC_DR_LEN", + + "init" + ] + + def load(self, bitfile): + self.prog.append("pld load 0 " + bitfile) + + def proxy(self, proxy_bitfile): + self.prog += [ + "pld load 0 " + proxy_bitfile, + "reset halt" + ] + + def flash_binary(self, flashno, address, filename): + self.prog += [ + "flash probe amc_xcu.spi{}".format(flashno), + "irscan amc_xcu.tap $XILINX_USER{}".format(flashno+1), + "flash write_bank {} {} 0x{:x}".format(flashno, filename, address) + ] + + def start(self): + self.proxy_loaded = False + self.prog.append("xcu_program xcu.tap") + + def main(): parser = get_argparser() opts = parser.parse_args() config = { "kc705": { - "chip": "xc7k325t", - "start": "xc7_program xc7.tap", - "gateware": 0x000000, - "bios": 0xaf0000, - "runtime": 0xb00000, - "storage": 0xb80000, + "programmer_factory": ProgrammerJtagSpi7, + "proxy_bitfile": "bscan_spi_xc7k325t.bit", + "adapters": ["nist_clock", "nist_qc2"], + "gateware": (0, 0x000000), + "bios": (0, 0xaf0000), + "runtime": (0, 0xb00000), + "storage": (0, 0xb80000), + }, + "sayma": { + "programmer_factory": ProgrammerSayma, + "proxy_bitfile": "bscan_spi_xcku040_sayma.bit", + "adapters": [], + "gateware": (0, 0x000000), + "bios": (1, 0x010000), + "runtime": (1, 0x020000), + "storage": (1, 0x0a0000), }, }[opts.target] - if opts.dir is None: - opts.dir = os.path.join(artiq_dir, "binaries", - "{}-{}".format(opts.target, opts.adapter)) - if not os.path.exists(opts.dir) and opts.action != ["start"]: + adapter = opts.adapter + if adapter is not None and adapter not in config["adapters"]: + raise SystemExit("Invalid adapter for this board") + if adapter is None and config["adapters"]: + adapter = config["adapters"][0] + bin_dir = opts.dir + if bin_dir is None: + if adapter is None: + bin_dir = os.path.join(artiq_dir, "binaries", + "{}".format(opts.target)) + else: + bin_dir = os.path.join(artiq_dir, "binaries", + "{}-{}".format(opts.target, adapter)) + if not os.path.exists(bin_dir) and opts.action != ["start"]: raise SystemExit("Binaries directory '{}' does not exist" - .format(opts.dir)) + .format(bin_dir)) + + if opts.target_file is None: + target_file = os.path.join("board", opts.target + ".cfg") + else: + target_file = opts.target_file + programmer = config["programmer_factory"](target_file) conv = False - - prog = [] - prog.append("init") for action in opts.action: if action == "proxy": - proxy_base = "bscan_spi_{}.bit".format(config["chip"]) - proxy = None - for p in [opts.dir, os.path.expanduser("~/.migen"), + proxy_found = False + for p in [bin_dir, os.path.expanduser("~/.migen"), "/usr/local/share/migen", "/usr/share/migen"]: - proxy_ = os.path.join(p, proxy_base) - if os.access(proxy_, os.R_OK): - proxy = "jtagspi_init 0 {{{}}}".format(proxy_) + proxy_bitfile = os.path.join(p, config["proxy_bitfile"]) + if os.access(proxy_bitfile, os.R_OK): + programmer.proxy(proxy_bitfile) + proxy_found = True break - if not proxy: + if not proxy_found: raise SystemExit( - "proxy gateware bitstream {} not found".format(proxy_base)) - prog.append(proxy) + "proxy gateware bitstream {} not found".format(config["proxy_bitfile"])) elif action == "gateware": - bin = os.path.join(opts.dir, "top.bin") + bin = os.path.join(bin_dir, "top.bin") if not os.access(bin, os.R_OK): bin_handle, bin = tempfile.mkstemp() - bit = os.path.join(opts.dir, "top.bit") + bit = os.path.join(bin_dir, "top.bit") conv = True - prog.append("jtagspi_program {{{}}} 0x{:x}".format( - bin, config["gateware"])) + programmer.flash_binary(*config["gateware"], bin) elif action == "bios": - prog.append("jtagspi_program {{{}}} 0x{:x}".format( - os.path.join(opts.dir, "bios.bin"), config["bios"])) + programmer.flash_binary(*config["bios"], os.path.join(bin_dir, "bios.bin")) elif action == "runtime": - prog.append("jtagspi_program {{{}}} 0x{:x}".format( - os.path.join(opts.dir, "runtime.fbi"), config["runtime"])) + programmer.flash_binary(*config["runtime"], os.path.join(bin_dir, "runtime.fbi")) elif action == "storage": - prog.append("jtagspi_program {{{}}} 0x{:x}".format( - opts.storage, config["storage"])) + programmer.flash_binary(*config["storage"], opts.storage) elif action == "load": - prog.append("pld load 0 {{{}}}".format( - os.path.join(opts.dir, "top.bit"))) + programmer.load(os.path.join(bin_dir, "top.bit")) elif action == "start": - prog.append(config["start"]) + programmer.start() else: raise ValueError("invalid action", action) - prog.append("exit") + + if conv: + bit2bin(bit, bin_handle) try: - if conv: - bit2bin(bit, bin_handle) - if opts.target_file is None: - target_file = os.path.join("board", opts.target + ".cfg") - else: - target_file = opts.target_file - subprocess.check_call([ - "openocd", - "-s", scripts_path(), - "-f", target_file, - "-c", "; ".join(prog), - ]) + programmer.do() finally: if conv: os.unlink(bin) From 261b6fb42e897cad9eef5c7879839805920d8ac5 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 20 Aug 2017 18:20:51 -0400 Subject: [PATCH 03/45] artiq_flash: fix AMC_DR_LEN --- artiq/frontend/artiq_flash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index daf48d27c..b73bc6006 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -136,7 +136,7 @@ class ProgrammerSayma(Programmer): "set XILINX_USER1 0x02", "set XILINX_USER2 0x03", - "set AMC_DR_LEN 2", + "set AMC_DR_LEN 1", "target create amc_xcu.proxy testee -chain-position amc_xcu.tap", "flash bank amc_xcu.spi0 jtagspi 0 0 0 0 amc_xcu.proxy $XILINX_USER1 $AMC_DR_LEN", From e94d0803e1174777b703119c996bebea9704d481 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 20 Aug 2017 18:21:36 -0400 Subject: [PATCH 04/45] artiq_flash: fix Sayma load addresses --- artiq/frontend/artiq_flash.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index b73bc6006..d6f279be0 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -185,9 +185,9 @@ def main(): "proxy_bitfile": "bscan_spi_xcku040_sayma.bit", "adapters": [], "gateware": (0, 0x000000), - "bios": (1, 0x010000), - "runtime": (1, 0x020000), - "storage": (1, 0x0a0000), + "bios": (1, 0x000000), + "runtime": (1, 0x010000), + "storage": (1, 0x090000), }, }[opts.target] From d6b624dfbe7afd5742d2de88e1457efe583e22cd Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 20 Aug 2017 19:01:55 -0400 Subject: [PATCH 05/45] sayma_amc: connect RTM serial and second serial --- artiq/gateware/targets/sayma_amc_standalone.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index 5370b69a6..d272a9cd9 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -44,6 +44,13 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): platform.request("user_led", 1))) self.csr_devices.append("leds") + serial_1 = platform.request("serial", 1) + serial_rtm = platform.request("serial_rtm") + self.comb += [ + serial_1.tx.eq(serial_rtm.rx), + serial_rtm.rx.eq(serial_1.tx) + ] + rtio_channels = [] for i in (2, 3): phy = ttl_simple.Output(platform.request("user_led", i)) From 44dc76e42e4b3f7b1c563e077058422ce5b193ac Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 21 Aug 2017 12:21:14 -0400 Subject: [PATCH 06/45] Add serial Wishbone bridge Files copied directly from https://github.com/enjoy-digital/sayma_test @ 9ce2cba87896d056819dc2edc54f0453a86162c3 --- artiq/gateware/serwb/etherbone.py | 722 ++++++++++++++++++++++++++++++ artiq/gateware/serwb/kusphy.py | 321 +++++++++++++ artiq/gateware/serwb/packet.py | 175 ++++++++ artiq/gateware/serwb/phy.py | 345 ++++++++++++++ artiq/gateware/serwb/s7phy.py | 325 ++++++++++++++ 5 files changed, 1888 insertions(+) create mode 100644 artiq/gateware/serwb/etherbone.py create mode 100644 artiq/gateware/serwb/kusphy.py create mode 100644 artiq/gateware/serwb/packet.py create mode 100644 artiq/gateware/serwb/phy.py create mode 100644 artiq/gateware/serwb/s7phy.py diff --git a/artiq/gateware/serwb/etherbone.py b/artiq/gateware/serwb/etherbone.py new file mode 100644 index 000000000..8337b4389 --- /dev/null +++ b/artiq/gateware/serwb/etherbone.py @@ -0,0 +1,722 @@ +""" +Etherbone + +CERN's Etherbone protocol is initially used to run a Wishbone bus over an +ethernet network. This re-implementation is meant to be run over serdes +and introduces some limitations: +- no probing (pf/pr) +- no address spaces (rca/bca/wca/wff) +- 32bits data and address +- 1 record per frame +""" + +from migen import * + +from misoc.interconnect import stream +from misoc.interconnect import wishbone + +from amc_rtm_link.packet import * + + +class Packetizer(Module): + def __init__(self, sink_description, source_description, header): + self.sink = sink = stream.Endpoint(sink_description) + self.source = source = stream.Endpoint(source_description) + self.header = Signal(header.length*8) + + # # # + + dw = len(self.sink.data) + + header_reg = Signal(header.length*8, reset_less=True) + header_words = (header.length*8)//dw + load = Signal() + shift = Signal() + counter = Signal(max=max(header_words, 2)) + counter_reset = Signal() + counter_ce = Signal() + self.sync += \ + If(counter_reset, + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + 1) + ) + + self.comb += header.encode(sink, self.header) + if header_words == 1: + self.sync += [ + If(load, + header_reg.eq(self.header) + ) + ] + else: + self.sync += [ + If(load, + header_reg.eq(self.header) + ).Elif(shift, + header_reg.eq(Cat(header_reg[dw:], Signal(dw))) + ) + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + if header_words == 1: + idle_next_state = "COPY" + else: + idle_next_state = "SEND_HEADER" + + fsm.act("IDLE", + sink.ack.eq(1), + counter_reset.eq(1), + If(sink.stb, + sink.ack.eq(0), + source.stb.eq(1), + source.eop.eq(0), + source.data.eq(self.header[:dw]), + If(source.stb & source.ack, + load.eq(1), + NextState(idle_next_state) + ) + ) + ) + if header_words != 1: + fsm.act("SEND_HEADER", + source.stb.eq(1), + source.eop.eq(0), + source.data.eq(header_reg[dw:2*dw]), + If(source.stb & source.ack, + shift.eq(1), + counter_ce.eq(1), + If(counter == header_words-2, + NextState("COPY") + ) + ) + ) + if hasattr(sink, "error"): + self.comb += source.error.eq(sink.error) + fsm.act("COPY", + source.stb.eq(sink.stb), + source.eop.eq(sink.eop), + source.data.eq(sink.data), + If(source.stb & source.ack, + sink.ack.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + + +class Depacketizer(Module): + def __init__(self, sink_description, source_description, header): + self.sink = sink = stream.Endpoint(sink_description) + self.source = source = stream.Endpoint(source_description) + self.header = Signal(header.length*8) + + # # # + + dw = len(sink.data) + + header_reg = Signal(header.length*8, reset_less=True) + header_words = (header.length*8)//dw + + shift = Signal() + counter = Signal(max=max(header_words, 2)) + counter_reset = Signal() + counter_ce = Signal() + self.sync += \ + If(counter_reset, + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + 1) + ) + + if header_words == 1: + self.sync += \ + If(shift, + header_reg.eq(sink.data) + ) + else: + self.sync += \ + If(shift, + header_reg.eq(Cat(header_reg[dw:], sink.data)) + ) + self.comb += self.header.eq(header_reg) + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + if header_words == 1: + idle_next_state = "COPY" + else: + idle_next_state = "RECEIVE_HEADER" + + fsm.act("IDLE", + sink.ack.eq(1), + counter_reset.eq(1), + If(sink.stb, + shift.eq(1), + NextState(idle_next_state) + ) + ) + if header_words != 1: + fsm.act("RECEIVE_HEADER", + sink.ack.eq(1), + If(sink.stb, + counter_ce.eq(1), + shift.eq(1), + If(counter == header_words-2, + NextState("COPY") + ) + ) + ) + no_payload = Signal() + self.sync += \ + If(fsm.before_entering("COPY"), + no_payload.eq(sink.eop) + ) + + if hasattr(sink, "error"): + self.comb += source.error.eq(sink.error) + self.comb += [ + source.eop.eq(sink.eop | no_payload), + source.data.eq(sink.data), + header.decode(self.header, source) + ] + fsm.act("COPY", + sink.ack.eq(source.ack), + source.stb.eq(sink.stb | no_payload), + If(source.stb & source.ack & source.eop, + NextState("IDLE") + ) + ) + + +etherbone_magic = 0x4e6f +etherbone_version = 1 +etherbone_packet_header_length = 8 +etherbone_packet_header_fields = { + "magic": HeaderField(0, 0, 16), + + "version": HeaderField(2, 4, 4), + "nr": HeaderField(2, 2, 1), + "pr": HeaderField(2, 1, 1), # unused + "pf": HeaderField(2, 0, 1), # unused + + "addr_size": HeaderField(3, 4, 4), # static + "port_size": HeaderField(3, 0, 4) # static +} +etherbone_packet_header = Header(etherbone_packet_header_fields, + etherbone_packet_header_length, + swap_field_bytes=True) + +etherbone_record_header_length = 4 +etherbone_record_header_fields = { + "bca": HeaderField(0, 0, 1), # unused + "rca": HeaderField(0, 1, 1), # unused + "rff": HeaderField(0, 2, 1), # unused + "cyc": HeaderField(0, 4, 1), # unused + "wca": HeaderField(0, 5, 1), # unused + "wff": HeaderField(0, 6, 1), # unused + + "byte_enable": HeaderField(1, 0, 8), + + "wcount": HeaderField(2, 0, 8), + + "rcount": HeaderField(3, 0, 8) +} +etherbone_record_header = Header(etherbone_record_header_fields, + etherbone_record_header_length, + swap_field_bytes=True) + +def _remove_from_layout(layout, *args): + r = [] + for f in layout: + remove = False + for arg in args: + if f[0] == arg: + remove = True + if not remove: + r.append(f) + return r + +def etherbone_packet_description(dw): + layout = etherbone_packet_header.get_layout() + layout += [("data", dw)] + return stream.EndpointDescription(layout) + +def etherbone_packet_user_description(dw): + layout = etherbone_packet_header.get_layout() + layout = _remove_from_layout(layout, + "magic", + "portsize", + "addrsize", + "version") + layout += user_description(dw).payload_layout + return stream.EndpointDescription(layout) + +def etherbone_record_description(dw): + layout = etherbone_record_header.get_layout() + layout += [("data", dw)] + return stream.EndpointDescription(layout) + +def etherbone_mmap_description(dw): + layout = [ + ("we", 1), + ("count", 8), + ("base_addr", 32), + ("be", dw//8), + ("addr", 32), + ("data", dw) + ] + return stream.EndpointDescription(layout) + + +# etherbone packet + +class EtherbonePacketPacketizer(Packetizer): + def __init__(self): + Packetizer.__init__(self, + etherbone_packet_description(32), + user_description(32), + etherbone_packet_header) + + +class EtherbonePacketTX(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32)) + self.source = source = stream.Endpoint(user_description(32)) + + # # # + + self.submodules.packetizer = packetizer = EtherbonePacketPacketizer() + self.comb += [ + packetizer.sink.stb.eq(sink.stb), + packetizer.sink.eop.eq(sink.eop), + sink.ack.eq(packetizer.sink.ack), + + packetizer.sink.magic.eq(etherbone_magic), + packetizer.sink.port_size.eq(32//8), + packetizer.sink.addr_size.eq(32//8), + packetizer.sink.nr.eq(sink.nr), + packetizer.sink.version.eq(etherbone_version), + + packetizer.sink.data.eq(sink.data) + ] + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + packetizer.source.ack.eq(1), + If(packetizer.source.stb, + packetizer.source.ack.eq(0), + NextState("SEND") + ) + ) + fsm.act("SEND", + packetizer.source.connect(source), + source.length.eq(sink.length + etherbone_packet_header.length), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + + +class EtherbonePacketDepacketizer(Depacketizer): + def __init__(self): + Depacketizer.__init__(self, + user_description(32), + etherbone_packet_description(32), + etherbone_packet_header) + + +class EtherbonePacketRX(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(user_description(32)) + self.source = source = stream.Endpoint(etherbone_packet_user_description(32)) + + # # # + + self.submodules.depacketizer = depacketizer = EtherbonePacketDepacketizer() + self.comb += sink.connect(depacketizer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + stb = Signal() + self.sync += stb.eq( + depacketizer.source.stb & + (depacketizer.source.magic == etherbone_magic) + ) + fsm.act("CHECK", + If(stb, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + self.comb += [ + source.eop.eq(depacketizer.source.eop), + + source.nr.eq(depacketizer.source.nr), + + source.data.eq(depacketizer.source.data), + + source.length.eq(sink.length - etherbone_packet_header.length) + ] + fsm.act("PRESENT", + source.stb.eq(depacketizer.source.stb), + depacketizer.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & + depacketizer.source.eop & + depacketizer.source.ack, + NextState("IDLE") + ) + ) + + +class EtherbonePacket(Module): + def __init__(self, port_sink, port_source): + self.submodules.tx = tx = EtherbonePacketTX() + self.submodules.rx = rx = EtherbonePacketRX() + self.comb += [ + tx.source.connect(port_sink), + port_source.connect(rx.sink) + ] + self.sink, self.source = self.tx.sink, self.rx.source + +# etherbone record + +class EtherboneRecordPacketizer(Packetizer): + def __init__(self): + Packetizer.__init__(self, + etherbone_record_description(32), + etherbone_packet_user_description(32), + etherbone_record_header) + + +class EtherboneRecordDepacketizer(Depacketizer): + def __init__(self): + Depacketizer.__init__(self, + etherbone_packet_user_description(32), + etherbone_record_description(32), + etherbone_record_header) + + +class EtherboneRecordReceiver(Module): + def __init__(self, buffer_depth=256): + self.sink = sink = stream.Endpoint(etherbone_record_description(32)) + self.source = source = stream.Endpoint(etherbone_mmap_description(32)) + + # # # + + fifo = stream.SyncFIFO(etherbone_record_description(32), buffer_depth, + buffered=True) + self.submodules += fifo + self.comb += sink.connect(fifo.sink) + + base_addr = Signal(32) + base_addr_update = Signal() + self.sync += If(base_addr_update, base_addr.eq(fifo.source.data)) + + counter = Signal(max=512) + counter_reset = Signal() + counter_ce = Signal() + self.sync += \ + If(counter_reset, + counter.eq(0) + ).Elif(counter_ce, + counter.eq(counter + 1) + ) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + fifo.source.ack.eq(1), + counter_reset.eq(1), + If(fifo.source.stb, + base_addr_update.eq(1), + If(fifo.source.wcount, + NextState("RECEIVE_WRITES") + ).Elif(fifo.source.rcount, + NextState("RECEIVE_READS") + + ) + ) + ) + fsm.act("RECEIVE_WRITES", + source.stb.eq(fifo.source.stb), + source.eop.eq(counter == fifo.source.wcount-1), + source.count.eq(fifo.source.wcount), + source.be.eq(fifo.source.byte_enable), + source.addr.eq(base_addr[2:] + counter), + source.we.eq(1), + source.data.eq(fifo.source.data), + fifo.source.ack.eq(source.ack), + If(source.stb & source.ack, + counter_ce.eq(1), + If(source.eop, + If(fifo.source.rcount, + NextState("RECEIVE_BASE_RET_ADDR") + ).Else( + NextState("IDLE") + ) + ) + ) + ) + fsm.act("RECEIVE_BASE_RET_ADDR", + counter_reset.eq(1), + If(fifo.source.stb, + base_addr_update.eq(1), + NextState("RECEIVE_READS") + ) + ) + fsm.act("RECEIVE_READS", + source.stb.eq(fifo.source.stb), + source.eop.eq(counter == fifo.source.rcount-1), + source.count.eq(fifo.source.rcount), + source.base_addr.eq(base_addr), + source.addr.eq(fifo.source.data[2:]), + fifo.source.ack.eq(source.ack), + If(source.stb & source.ack, + counter_ce.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + + +class EtherboneRecordSender(Module): + def __init__(self, buffer_depth=256): + self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) + self.source = source = stream.Endpoint(etherbone_record_description(32)) + + # # # + + pbuffer = stream.SyncFIFO(etherbone_mmap_description(32), buffer_depth) + self.submodules += pbuffer + self.comb += sink.connect(pbuffer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + pbuffer.source.ack.eq(1), + If(pbuffer.source.stb, + pbuffer.source.ack.eq(0), + NextState("SEND_BASE_ADDRESS") + ) + ) + self.comb += [ + source.byte_enable.eq(pbuffer.source.be), + If(pbuffer.source.we, + source.wcount.eq(pbuffer.source.count) + ).Else( + source.rcount.eq(pbuffer.source.count) + ) + ] + + fsm.act("SEND_BASE_ADDRESS", + source.stb.eq(pbuffer.source.stb), + source.eop.eq(0), + source.data.eq(pbuffer.source.base_addr), + If(source.ack, + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + source.stb.eq(pbuffer.source.stb), + source.eop.eq(pbuffer.source.eop), + source.data.eq(pbuffer.source.data), + If(source.stb & source.ack, + pbuffer.source.ack.eq(1), + If(source.eop, + NextState("IDLE") + ) + ) + ) + + +class EtherboneRecord(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32)) + self.source = source = stream.Endpoint(etherbone_packet_user_description(32)) + + # # # + + # receive record, decode it and generate mmap stream + self.submodules.depacketizer = depacketizer = EtherboneRecordDepacketizer() + self.submodules.receiver = receiver = EtherboneRecordReceiver() + self.comb += [ + sink.connect(depacketizer.sink), + depacketizer.source.connect(receiver.sink) + ] + + # receive mmap stream, encode it and send records + self.submodules.sender = sender = EtherboneRecordSender() + self.submodules.packetizer = packetizer = EtherboneRecordPacketizer() + self.comb += [ + sender.source.connect(packetizer.sink), + packetizer.source.connect(source), + source.length.eq(etherbone_record_header.length + + (sender.source.wcount != 0)*4 + sender.source.wcount*4 + + (sender.source.rcount != 0)*4 + sender.source.rcount*4) + ] + + +# etherbone wishbone + +class EtherboneWishboneMaster(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) + self.source = source = stream.Endpoint(etherbone_mmap_description(32)) + self.bus = bus = wishbone.Interface() + + # # # + + data = Signal(32) + data_update = Signal() + self.sync += If(data_update, data.eq(bus.dat_r)) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb, + sink.ack.eq(0), + If(sink.we, + NextState("WRITE_DATA") + ).Else( + NextState("READ_DATA") + ) + ) + ) + fsm.act("WRITE_DATA", + bus.adr.eq(sink.addr), + bus.dat_w.eq(sink.data), + bus.sel.eq(sink.be), + bus.stb.eq(sink.stb), + bus.we.eq(1), + bus.cyc.eq(1), + If(bus.stb & bus.ack, + sink.ack.eq(1), + If(sink.eop, + NextState("IDLE") + ) + ) + ) + fsm.act("READ_DATA", + bus.adr.eq(sink.addr), + bus.sel.eq(sink.be), + bus.stb.eq(sink.stb), + bus.cyc.eq(1), + If(bus.stb & bus.ack, + data_update.eq(1), + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + source.stb.eq(sink.stb), + source.eop.eq(sink.eop), + source.base_addr.eq(sink.base_addr), + source.addr.eq(sink.addr), + source.count.eq(sink.count), + source.be.eq(sink.be), + source.we.eq(1), + source.data.eq(data), + If(source.stb & source.ack, + sink.ack.eq(1), + If(source.eop, + NextState("IDLE") + ).Else( + NextState("READ_DATA") + ) + ) + ) + + +class EtherboneWishboneSlave(Module): + def __init__(self): + self.bus = bus = wishbone.Interface() + self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) + self.source = source = stream.Endpoint(etherbone_mmap_description(32)) + + # # # + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(bus.stb & bus.cyc, + If(bus.we, + NextState("SEND_WRITE") + ).Else( + NextState("SEND_READ") + ) + ) + ) + fsm.act("SEND_WRITE", + source.stb.eq(1), + source.eop.eq(1), + source.base_addr[2:].eq(bus.adr), + source.count.eq(1), + source.be.eq(bus.sel), + source.we.eq(1), + source.data.eq(bus.dat_w), + If(source.stb & source.ack, + bus.ack.eq(1), + NextState("IDLE") + ) + ) + fsm.act("SEND_READ", + source.stb.eq(1), + source.eop.eq(1), + source.base_addr.eq(0), + source.count.eq(1), + source.be.eq(bus.sel), + source.we.eq(0), + source.data[2:].eq(bus.adr), + If(source.stb & source.ack, + NextState("WAIT_READ") + ) + ) + fsm.act("WAIT_READ", + sink.ack.eq(1), + If(sink.stb & sink.we, + bus.ack.eq(1), + bus.dat_r.eq(sink.data), + NextState("IDLE") + ) + ) + + +# etherbone + +class Etherbone(Module): + def __init__(self, mode="master"): + self.sink = sink = stream.Endpoint(user_description(32)) + self.source = source = stream.Endpoint(user_description(32)) + + # # # + + self.submodules.packet = EtherbonePacket(source, sink) + self.submodules.record = EtherboneRecord() + if mode == "master": + self.submodules.wishbone = EtherboneWishboneMaster() + elif mode == "slave": + self.submodules.wishbone = EtherboneWishboneSlave() + else: + raise ValueError + + self.comb += [ + self.packet.source.connect(self.record.sink), + self.record.source.connect(self.packet.sink), + self.record.receiver.source.connect(self.wishbone.sink), + self.wishbone.source.connect(self.record.sender.sink) + ] diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py new file mode 100644 index 000000000..ed3716491 --- /dev/null +++ b/artiq/gateware/serwb/kusphy.py @@ -0,0 +1,321 @@ +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.cdc import MultiReg, PulseSynchronizer, Gearbox +from migen.genlib.misc import BitSlip + +from misoc.cores.code_8b10b import Encoder, Decoder + +from amc_rtm_link.phy import PhaseDetector + + +class KUSSerdesPLL(Module): + def __init__(self, refclk_freq, linerate, vco_div=1): + assert refclk_freq == 125e6 + assert linerate == 1.25e9 + + self.lock = Signal() + self.refclk = Signal() + self.serdes_clk = Signal() + self.serdes_20x_clk = Signal() + self.serdes_5x_clk = Signal() + + # # # + + #---------------------- + # refclk: 125MHz + # vco: 1250MHz + #---------------------- + # serdes: 31.25MHz + # serdes_20x: 625MHz + # serdes_5x: 156.25MHz + #---------------------- + self.linerate = linerate + + pll_locked = Signal() + pll_fb = Signal() + pll_serdes_clk = Signal() + pll_serdes_20x_clk = Signal() + pll_serdes_5x_clk = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1.25GHz / vco_div + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0, + p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div, + i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, + o_CLKFBOUT=pll_fb, + + # 31.25MHz: serdes + p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, + o_CLKOUT0=pll_serdes_clk, + + # 625MHz: serdes_20x + p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, + o_CLKOUT1=pll_serdes_20x_clk, + + # 156.25MHz: serdes_5x + p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, + o_CLKOUT2=pll_serdes_5x_clk + ), + Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), + Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), + Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) + ] + self.specials += MultiReg(pll_locked, self.lock) + + +class KUSSerdes(Module): + def __init__(self, pll, pads, mode="master"): + self.tx_data = Signal(32) + self.rx_data = Signal(32) + + self.tx_idle = Signal() + self.tx_comma = Signal() + self.rx_idle = Signal() + self.rx_comma = Signal() + + self.rx_bitslip_value = Signal(6) + self.rx_delay_rst = Signal() + self.rx_delay_inc = Signal() + self.rx_delay_ce = Signal() + self.rx_delay_en_vtc = Signal() + + # # # + + self.submodules.encoder = ClockDomainsRenamer("serdes")( + Encoder(4, True)) + self.decoders = [ClockDomainsRenamer("serdes")( + Decoder(True)) for _ in range(4)] + self.submodules += self.decoders + + # clocking: + + # In master mode: + # - linerate/10 pll refclk provided by user + # - linerate/10 slave refclk generated on clk_pads + # In Slave mode: + # - linerate/10 pll refclk provided by clk_pads + self.clock_domains.cd_serdes = ClockDomain() + self.clock_domains.cd_serdes_5x = ClockDomain() + self.clock_domains.cd_serdes_20x = ClockDomain(reset_less=True) + self.comb += [ + self.cd_serdes.clk.eq(pll.serdes_clk), + self.cd_serdes_5x.clk.eq(pll.serdes_5x_clk), + self.cd_serdes_20x.clk.eq(pll.serdes_20x_clk) + ] + self.specials += AsyncResetSynchronizer(self.cd_serdes, ~pll.lock) + self.comb += self.cd_serdes_5x.rst.eq(self.cd_serdes.rst) + + # control/status cdc + tx_idle = Signal() + tx_comma = Signal() + rx_idle = Signal() + rx_comma = Signal() + rx_bitslip_value = Signal(6) + rx_delay_rst = Signal() + rx_delay_inc = Signal() + rx_delay_en_vtc = Signal() + rx_delay_ce = Signal() + self.specials += [ + MultiReg(self.tx_idle, tx_idle, "serdes"), + MultiReg(self.tx_comma, tx_comma, "serdes"), + MultiReg(rx_idle, self.rx_idle, "sys"), + MultiReg(rx_comma, self.rx_comma, "sys"), + MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serdes"), + MultiReg(self.rx_delay_inc, rx_delay_inc, "serdes_5x"), + MultiReg(self.rx_delay_en_vtc, rx_delay_en_vtc, "serdes_5x") + ] + self.submodules.do_rx_delay_rst = PulseSynchronizer("sys", "serdes_5x") + self.comb += [ + rx_delay_rst.eq(self.do_rx_delay_rst.o), + self.do_rx_delay_rst.i.eq(self.rx_delay_rst) + ] + self.submodules.do_rx_delay_ce = PulseSynchronizer("sys", "serdes_5x") + self.comb += [ + rx_delay_ce.eq(self.do_rx_delay_ce.o), + self.do_rx_delay_ce.i.eq(self.rx_delay_ce) + ] + + # tx clock (linerate/10) + if mode == "master": + self.submodules.tx_clk_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) | + (0b1111100000 << 20) | + (0b1111100000 << 10) | + (0b1111100000 << 0)) + clk_o = Signal() + self.specials += [ + Instance("OSERDESE3", + p_DATA_WIDTH=8, p_INIT=0, + p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, + + o_OQ=clk_o, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D=self.tx_clk_gearbox.o + ), + Instance("OBUFDS", + i_I=clk_o, + o_O=pads.clk_p, + o_OB=pads.clk_n + ) + ] + + # tx datapath + # tx_data -> encoders -> gearbox -> serdes + self.submodules.tx_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += [ + If(tx_comma, + self.encoder.k[0].eq(1), + self.encoder.d[0].eq(0xbc) + ).Else( + self.encoder.d[0].eq(self.tx_data[0:8]), + self.encoder.d[1].eq(self.tx_data[8:16]), + self.encoder.d[2].eq(self.tx_data[16:24]), + self.encoder.d[3].eq(self.tx_data[24:32]) + ) + ] + self.sync.serdes += \ + If(tx_idle, + self.tx_gearbox.i.eq(0) + ).Else( + self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)])) + ) + + serdes_o = Signal() + self.specials += [ + Instance("OSERDESE3", + p_DATA_WIDTH=8, p_INIT=0, + p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, + + o_OQ=serdes_o, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D=self.tx_gearbox.o + ), + Instance("OBUFDS", + i_I=serdes_o, + o_O=pads.tx_p, + o_OB=pads.tx_n + ) + ] + + # rx clock + use_bufr = True + if mode == "slave": + clk_i = Signal() + clk_i_bufg = Signal() + self.specials += [ + Instance("IBUFDS", + i_I=pads.clk_p, + i_IB=pads.clk_n, + o_O=clk_i + ) + ] + if use_bufr: + clk_i_bufr = Signal() + self.specials += [ + Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr), + Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg) + ] + else: + self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg) + self.comb += pll.refclk.eq(clk_i_bufg) + + # rx datapath + # serdes -> gearbox -> bitslip -> decoders -> rx_data + self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") + self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) + + self.submodules.phase_detector = ClockDomainsRenamer("serdes_5x")(PhaseDetector()) + + # 2 serdes for phase detection: 1 master (used for data) / 1 slave + serdes_m_i_nodelay = Signal() + serdes_s_i_nodelay = Signal() + self.specials += [ + Instance("IBUFDS_DIFF_OUT", + i_I=pads.rx_p, + i_IB=pads.rx_n, + o_O=serdes_m_i_nodelay, + o_OB=serdes_s_i_nodelay + ) + ] + + serdes_m_i_delayed = Signal() + serdes_m_q = Signal(8) + self.specials += [ + Instance("IDELAYE3", + p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0, + p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0, + p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN", + p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0, + + i_CLK=ClockSignal("serdes_5x"), + i_RST=rx_delay_rst, i_LOAD=0, + i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, + i_CE=rx_delay_ce, + + i_IDATAIN=serdes_m_i_nodelay, o_DATAOUT=serdes_m_i_delayed + ), + Instance("ISERDESE3", + p_DATA_WIDTH=8, + + i_D=serdes_m_i_delayed, + i_RST=ResetSignal("serdes"), + i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, + i_CLK=ClockSignal("serdes_20x"), i_CLK_B=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + o_Q=serdes_m_q + ) + ] + self.comb += self.phase_detector.mdata.eq(serdes_m_q) + + serdes_s_i_delayed = Signal() + serdes_s_q = Signal(8) + self.specials += [ + Instance("IDELAYE3", + p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0, + p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0, + # Note: can't use TIME mode since not reloading DELAY_VALUE on rst... + # Got answer from Xilinx, need testing: + # https://forums.xilinx.com/xlnx/board/crawl_message?board.id=ultrascale&message.id=4699 + p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN", + p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=50, # 1/4 bit period (ambient temp) + + i_CLK=ClockSignal("serdes_5x"), + i_RST=rx_delay_rst, i_LOAD=0, + i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, + i_CE=rx_delay_ce, + + i_IDATAIN=serdes_s_i_nodelay, o_DATAOUT=serdes_s_i_delayed + ), + Instance("ISERDESE3", + p_DATA_WIDTH=8, + + i_D=serdes_s_i_delayed, + i_RST=ResetSignal("serdes"), + i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, + i_CLK=ClockSignal("serdes_20x"), i_CLK_B=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + o_Q=serdes_s_q + ) + ] + self.comb += self.phase_detector.sdata.eq(~serdes_s_q) + + self.comb += [ + self.rx_gearbox.i.eq(serdes_m_q), + self.rx_bitslip.value.eq(rx_bitslip_value), + self.rx_bitslip.i.eq(self.rx_gearbox.o), + self.decoders[0].input.eq(self.rx_bitslip.o[0:10]), + self.decoders[1].input.eq(self.rx_bitslip.o[10:20]), + self.decoders[2].input.eq(self.rx_bitslip.o[20:30]), + self.decoders[3].input.eq(self.rx_bitslip.o[30:40]), + self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])), + rx_idle.eq(self.rx_bitslip.o == 0), + rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) & + ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) & + ((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0)) & + ((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0))) + + ] diff --git a/artiq/gateware/serwb/packet.py b/artiq/gateware/serwb/packet.py new file mode 100644 index 000000000..a1d2b00fb --- /dev/null +++ b/artiq/gateware/serwb/packet.py @@ -0,0 +1,175 @@ +from math import ceil +from copy import copy +from collections import OrderedDict + +from migen import * +from migen.genlib.misc import WaitTimer + +from misoc.interconnect import stream +from misoc.interconnect.stream import EndpointDescription + + +def reverse_bytes(signal): + n = ceil(len(signal)/8) + return Cat(iter([signal[i*8:(i+1)*8] for i in reversed(range(n))])) + + +class HeaderField: + def __init__(self, byte, offset, width): + self.byte = byte + self.offset = offset + self.width = width + + +class Header: + def __init__(self, fields, length, swap_field_bytes=True): + self.fields = fields + self.length = length + self.swap_field_bytes = swap_field_bytes + + def get_layout(self): + layout = [] + for k, v in sorted(self.fields.items()): + layout.append((k, v.width)) + return layout + + def get_field(self, obj, name, width): + if "_lsb" in name: + field = getattr(obj, name.replace("_lsb", ""))[:width] + elif "_msb" in name: + field = getattr(obj, name.replace("_msb", ""))[width:2*width] + else: + field = getattr(obj, name) + if len(field) != width: + raise ValueError("Width mismatch on " + name + " field") + return field + + def encode(self, obj, signal): + r = [] + for k, v in sorted(self.fields.items()): + start = v.byte*8 + v.offset + end = start + v.width + field = self.get_field(obj, k, v.width) + if self.swap_field_bytes: + field = reverse_bytes(field) + r.append(signal[start:end].eq(field)) + return r + + def decode(self, signal, obj): + r = [] + for k, v in sorted(self.fields.items()): + start = v.byte*8 + v.offset + end = start + v.width + field = self.get_field(obj, k, v.width) + if self.swap_field_bytes: + r.append(field.eq(reverse_bytes(signal[start:end]))) + else: + r.append(field.eq(signal[start:end])) + return r + +def phy_description(dw): + layout = [("data", dw)] + return stream.EndpointDescription(layout) + + +def user_description(dw): + layout = [ + ("data", 32), + ("length", 32) + ] + return stream.EndpointDescription(layout) + + +class Packetizer(Module): + def __init__(self): + self.sink = sink = stream.Endpoint(user_description(32)) + self.source = source = stream.Endpoint(phy_description(32)) + + # # # + + # Packet description + # - preamble : 4 bytes + # - length : 4 bytes + # - payload + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + If(sink.stb, + NextState("INSERT_PREAMBLE") + ) + ) + fsm.act("INSERT_PREAMBLE", + source.stb.eq(1), + source.data.eq(0x5aa55aa5), + If(source.ack, + NextState("INSERT_LENGTH") + ) + ) + fsm.act("INSERT_LENGTH", + source.stb.eq(1), + source.data.eq(sink.length), + If(source.ack, + NextState("COPY") + ) + ) + fsm.act("COPY", + source.stb.eq(sink.stb), + source.data.eq(sink.data), + sink.ack.eq(source.ack), + If(source.ack & sink.eop, + NextState("IDLE") + ) + ) + + +class Depacketizer(Module): + def __init__(self, clk_freq, timeout=10): + self.sink = sink = stream.Endpoint(phy_description(32)) + self.source = source = stream.Endpoint(user_description(32)) + + # # # + + # Packet description + # - preamble : 4 bytes + # - length : 4 bytes + # - payload + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + self.submodules.timer = WaitTimer(clk_freq*timeout) + self.comb += self.timer.wait.eq(~fsm.ongoing("IDLE")) + + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & (sink.data == 0x5aa55aa5), + NextState("RECEIVE_LENGTH") + ) + ) + fsm.act("RECEIVE_LENGTH", + sink.ack.eq(1), + If(sink.stb, + NextValue(source.length, sink.data), + NextState("COPY") + ) + ) + eop = Signal() + cnt = Signal(32) + fsm.act("COPY", + source.stb.eq(sink.stb), + source.eop.eq(eop), + source.data.eq(sink.data), + sink.ack.eq(source.ack), + If((source.stb & source.ack & eop) | self.timer.done, + NextState("IDLE") + ) + ) + self.sync += \ + If(fsm.ongoing("IDLE"), + cnt.eq(0) + ).Elif(source.stb & source.ack, + cnt.eq(cnt + 1) + ) + self.comb += eop.eq(cnt == source.length[2:] - 1) diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py new file mode 100644 index 000000000..39640208d --- /dev/null +++ b/artiq/gateware/serwb/phy.py @@ -0,0 +1,345 @@ +from migen import * +from migen.genlib.cdc import MultiReg, PulseSynchronizer +from migen.genlib.misc import WaitTimer + +from misoc.interconnect.csr import * + + +class PhaseDetector(Module, AutoCSR): + def __init__(self, nbits=8): + self.mdata = Signal(8) + self.sdata = Signal(8) + + self.reset = Signal() + self.too_early = Signal() + self.too_late = Signal() + + # # # + + # Ideal sampling (middle of the eye): + # _____ _____ _____ + # | |_____| |_____| |_____| data + # + + + + + + master sampling + # - - - - - - slave sampling (90°/bit period) + # Since taps are fixed length delays, this ideal case is not possible + # and we will fall in the 2 following possible cases: + # + # 1) too late sampling (idelay needs to be decremented): + # _____ _____ _____ + # | |_____| |_____| |_____| data + # + + + + + + master sampling + # - - - - - - slave sampling (90°/bit period) + # On mdata transition, mdata != sdata + # + # + # 2) too early sampling (idelay needs to be incremented): + # _____ _____ _____ + # | |_____| |_____| |_____| data + # + + + + + + master sampling + # - - - - - - slave sampling (90°/bit period) + # On mdata transition, mdata == sdata + + transition = Signal() + inc = Signal() + dec = Signal() + + # Find transition + mdata_d = Signal(8) + self.sync.serdes_5x += mdata_d.eq(self.mdata) + self.comb += transition.eq(mdata_d != self.mdata) + + + # Find what to do + self.comb += [ + inc.eq(transition & (self.mdata == self.sdata)), + dec.eq(transition & (self.mdata != self.sdata)) + ] + + # Error accumulator + lateness = Signal(nbits, reset=2**(nbits - 1)) + too_late = Signal() + too_early = Signal() + reset_lateness = Signal() + self.comb += [ + too_late.eq(lateness == (2**nbits - 1)), + too_early.eq(lateness == 0) + ] + self.sync.serdes_5x += [ + If(reset_lateness, + lateness.eq(2**(nbits - 1)) + ).Elif(~too_late & ~too_early, + If(inc, lateness.eq(lateness - 1)), + If(dec, lateness.eq(lateness + 1)) + ) + ] + + # control / status cdc + self.specials += [ + MultiReg(too_early, self.too_early), + MultiReg(too_late, self.too_late) + ] + self.submodules.do_reset_lateness = PulseSynchronizer("sys", "serdes_5x") + self.comb += [ + reset_lateness.eq(self.do_reset_lateness.o), + self.do_reset_lateness.i.eq(self.reset) + ] + + +# Master <--> Slave synchronization: +# 1) Master sends idle pattern (zeroes) to reset Slave. +# 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle pattern. +# 3) Slave sends K28.5 commas to allow Master to calibrate, Master sends K28.5 commas. +# 4) Master stops sending K28.5 commas. +# 5) Slave stops sending K25.5 commas. +# 6) Link is ready. + +class SerdesMasterInit(Module): + def __init__(self, serdes, taps): + self.reset = Signal() + self.error = Signal() + self.ready = Signal() + + # # # + + self.delay = delay = Signal(max=taps) + self.delay_found = delay_found = Signal() + self.bitslip = bitslip = Signal(max=40) + self.bitslip_found = bitslip_found = Signal() + + timer = WaitTimer(8192) + self.submodules += timer + + self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) + self.comb += self.fsm.reset.eq(self.reset) + + phase_detector_too_early_last = Signal() + + fsm.act("IDLE", + NextValue(delay_found, 0), + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1), + NextValue(bitslip_found, 0), + NextValue(bitslip, 0), + NextValue(phase_detector_too_early_last, 0), + NextState("RESET_SLAVE"), + serdes.tx_idle.eq(1) + ) + fsm.act("RESET_SLAVE", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + NextState("SEND_PATTERN") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("SEND_PATTERN", + If(~serdes.rx_idle, + NextState("WAIT_STABLE") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("WAIT_STABLE", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + serdes.phase_detector.reset.eq(1), + If(~delay_found, + NextState("CHECK_PHASE") + ).Else( + NextState("CHECK_PATTERN") + ), + ), + serdes.tx_comma.eq(1) + ) + fsm.act("CHECK_PHASE", + # Since we are always incrementing delay, + # ideal sampling is found when phase detector + # transitions from too_early to too_late + If(serdes.phase_detector.too_late & + phase_detector_too_early_last, + NextValue(delay_found, 1), + NextState("CHECK_PATTERN") + ).Elif(serdes.phase_detector.too_late | + serdes.phase_detector.too_early, + NextValue(phase_detector_too_early_last, + serdes.phase_detector.too_early), + NextState("INC_DELAY") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("INC_DELAY", + If(delay == (taps - 1), + NextState("ERROR") + ).Else( + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1), + NextState("WAIT_STABLE") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("CHECK_PATTERN", + If(serdes.rx_comma, + timer.wait.eq(1), + If(timer.done, + NextValue(bitslip_found, 1), + NextState("READY") + ) + ).Else( + NextState("INC_BITSLIP") + ), + serdes.tx_comma.eq(1) + ) + self.comb += serdes.rx_bitslip_value.eq(bitslip) + fsm.act("INC_BITSLIP", + If(bitslip == (40 - 1), + NextState("ERROR") + ).Else( + NextValue(bitslip, bitslip + 1), + NextState("WAIT_STABLE") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("READY", + self.ready.eq(1) + ) + fsm.act("ERROR", + self.error.eq(1) + ) + + +class SerdesSlaveInit(Module, AutoCSR): + def __init__(self, serdes, taps): + self.reset = Signal() + self.ready = Signal() + self.error = Signal() + + # # # + + self.delay = delay = Signal(max=taps) + self.delay_found = delay_found = Signal() + self.bitslip = bitslip = Signal(max=40) + self.bitslip_found = bitslip_found = Signal() + + timer = WaitTimer(1024) + self.submodules += timer + + self.comb += self.reset.eq(serdes.rx_idle) + + self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) + + phase_detector_too_early_last = Signal() + + fsm.act("IDLE", + NextValue(delay_found, 0), + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1), + NextValue(bitslip_found, 0), + NextValue(bitslip, 0), + NextValue(phase_detector_too_early_last, 0), + NextState("WAIT_STABLE"), + serdes.tx_idle.eq(1) + ) + fsm.act("WAIT_STABLE", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + serdes.phase_detector.reset.eq(1), + If(~delay_found, + NextState("CHECK_PHASE") + ).Else( + NextState("CHECK_PATTERN") + ), + ), + serdes.tx_idle.eq(1) + ) + fsm.act("CHECK_PHASE", + # Since we are always incrementing delay, + # ideal sampling is found when phase detector + # transitions from too_early to too_late + If(serdes.phase_detector.too_late & + phase_detector_too_early_last, + NextValue(delay_found, 1), + NextState("CHECK_PATTERN") + ).Elif(serdes.phase_detector.too_late | + serdes.phase_detector.too_early, + NextValue(phase_detector_too_early_last, + serdes.phase_detector.too_early), + NextState("INC_DELAY") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("INC_DELAY", + If(delay == (taps - 1), + NextState("ERROR") + ).Else( + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1), + NextState("WAIT_STABLE") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("CHECK_PATTERN", + If(serdes.rx_comma, + timer.wait.eq(1), + If(timer.done, + NextValue(bitslip_found, 1), + NextState("SEND_PATTERN") + ) + ).Else( + NextState("INC_BITSLIP") + ), + serdes.tx_idle.eq(1) + ) + self.comb += serdes.rx_bitslip_value.eq(bitslip) + fsm.act("INC_BITSLIP", + If(bitslip == (40 - 1), + NextState("ERROR") + ).Else( + NextValue(bitslip, bitslip + 1), + NextState("WAIT_STABLE") + ), + serdes.tx_idle.eq(1) + ) + fsm.act("SEND_PATTERN", + timer.wait.eq(1), + If(timer.done, + If(~serdes.rx_comma, + NextState("READY") + ) + ), + serdes.tx_comma.eq(1) + ) + fsm.act("READY", + self.ready.eq(1) + ) + fsm.act("ERROR", + self.error.eq(1) + ) + + +class SerdesControl(Module, AutoCSR): + def __init__(self, init, mode="master"): + if mode == "master": + self.reset = CSR() + self.ready = CSRStatus() + self.error = CSRStatus() + + self.delay = CSRStatus(9) + self.delay_found = CSRStatus() + self.bitslip = CSRStatus(6) + self.bitslip_found = CSRStatus() + + # # # + + if mode == "master": + self.comb += init.reset.eq(self.reset.re) + self.comb += [ + self.ready.status.eq(init.ready), + self.error.status.eq(init.error), + self.delay_found.status.eq(init.delay_found), + self.delay.status.eq(init.delay), + self.bitslip_found.status.eq(init.bitslip_found), + self.bitslip.status.eq(init.bitslip) + ] diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py new file mode 100644 index 000000000..861c38d05 --- /dev/null +++ b/artiq/gateware/serwb/s7phy.py @@ -0,0 +1,325 @@ +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.cdc import MultiReg, Gearbox +from migen.genlib.misc import BitSlip + +from misoc.cores.code_8b10b import Encoder, Decoder + +from amc_rtm_link.phy import PhaseDetector + + +class S7SerdesPLL(Module): + def __init__(self, refclk_freq, linerate, vco_div=1): + assert refclk_freq == 125e6 + assert linerate == 1.25e9 + + self.lock = Signal() + self.refclk = Signal() + self.serdes_clk = Signal() + self.serdes_20x_clk = Signal() + self.serdes_5x_clk = Signal() + + # # # + + #---------------------- + # refclk: 125MHz + # vco: 1250MHz + #---------------------- + # serdes: 31.25MHz + # serdes_20x: 625MHz + # serdes_5x: 156.25MHz + #---------------------- + self.linerate = linerate + + pll_locked = Signal() + pll_fb = Signal() + pll_serdes_clk = Signal() + pll_serdes_20x_clk = Signal() + pll_serdes_5x_clk = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1.25GHz / vco_div + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0, + p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div, + i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, + o_CLKFBOUT=pll_fb, + + # 31.25MHz: serdes + p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, + o_CLKOUT0=pll_serdes_clk, + + # 625MHz: serdes_20x + p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, + o_CLKOUT1=pll_serdes_20x_clk, + + # 156.25MHz: serdes_5x + p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, + o_CLKOUT2=pll_serdes_5x_clk + ), + Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), + Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), + Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) + ] + self.specials += MultiReg(pll_locked, self.lock) + + +class S7Serdes(Module): + def __init__(self, pll, pads, mode="master"): + self.tx_data = Signal(32) + self.rx_data = Signal(32) + + self.tx_idle = Signal() + self.tx_comma = Signal() + self.rx_idle = Signal() + self.rx_comma = Signal() + + self.rx_bitslip_value = Signal(6) + self.rx_delay_rst = Signal() + self.rx_delay_inc = Signal() + self.rx_delay_ce = Signal() + + # # # + + self.submodules.encoder = ClockDomainsRenamer("serdes")( + Encoder(4, True)) + self.decoders = [ClockDomainsRenamer("serdes")( + Decoder(True)) for _ in range(4)] + self.submodules += self.decoders + + # clocking: + + # In master mode: + # - linerate/10 pll refclk provided by user + # - linerate/10 slave refclk generated on clk_pads + # In Slave mode: + # - linerate/10 pll refclk provided by clk_pads + self.clock_domains.cd_serdes = ClockDomain() + self.clock_domains.cd_serdes_5x = ClockDomain() + self.clock_domains.cd_serdes_20x = ClockDomain(reset_less=True) + self.comb += [ + self.cd_serdes.clk.eq(pll.serdes_clk), + self.cd_serdes_5x.clk.eq(pll.serdes_5x_clk), + self.cd_serdes_20x.clk.eq(pll.serdes_20x_clk) + ] + self.specials += AsyncResetSynchronizer(self.cd_serdes, ~pll.lock) + self.comb += self.cd_serdes_5x.rst.eq(self.cd_serdes.rst) + + # control/status cdc + tx_idle = Signal() + tx_comma = Signal() + rx_idle = Signal() + rx_comma = Signal() + rx_bitslip_value = Signal(6) + self.specials += [ + MultiReg(self.tx_idle, tx_idle, "serdes"), + MultiReg(self.tx_comma, tx_comma, "serdes"), + MultiReg(rx_idle, self.rx_idle, "sys"), + MultiReg(rx_comma, self.rx_comma, "sys") + ] + self.specials += MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serdes"), + + # tx clock (linerate/10) + if mode == "master": + self.submodules.tx_clk_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) | + (0b1111100000 << 20) | + (0b1111100000 << 10) | + (0b1111100000 << 0)) + clk_o = Signal() + self.specials += [ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=clk_o, + i_OCE=1, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D1=self.tx_clk_gearbox.o[0], i_D2=self.tx_clk_gearbox.o[1], + i_D3=self.tx_clk_gearbox.o[2], i_D4=self.tx_clk_gearbox.o[3], + i_D5=self.tx_clk_gearbox.o[4], i_D6=self.tx_clk_gearbox.o[5], + i_D7=self.tx_clk_gearbox.o[6], i_D8=self.tx_clk_gearbox.o[7] + ), + Instance("OBUFDS", + i_I=clk_o, + o_O=pads.clk_p, + o_OB=pads.clk_n + ) + ] + + # tx datapath + # tx_data -> encoders -> gearbox -> serdes + self.submodules.tx_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.comb += [ + If(tx_comma, + self.encoder.k[0].eq(1), + self.encoder.d[0].eq(0xbc) + ).Else( + self.encoder.d[0].eq(self.tx_data[0:8]), + self.encoder.d[1].eq(self.tx_data[8:16]), + self.encoder.d[2].eq(self.tx_data[16:24]), + self.encoder.d[3].eq(self.tx_data[24:32]) + ) + ] + self.sync.serdes += \ + If(tx_idle, + self.tx_gearbox.i.eq(0) + ).Else( + self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)])) + ) + + serdes_o = Signal() + self.specials += [ + Instance("OSERDESE2", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_SERDES_MODE="MASTER", + + o_OQ=serdes_o, + i_OCE=1, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_D1=self.tx_gearbox.o[0], i_D2=self.tx_gearbox.o[1], + i_D3=self.tx_gearbox.o[2], i_D4=self.tx_gearbox.o[3], + i_D5=self.tx_gearbox.o[4], i_D6=self.tx_gearbox.o[5], + i_D7=self.tx_gearbox.o[6], i_D8=self.tx_gearbox.o[7] + ), + Instance("OBUFDS", + i_I=serdes_o, + o_O=pads.tx_p, + o_OB=pads.tx_n + ) + ] + + # rx clock + use_bufr = True + if mode == "slave": + clk_i = Signal() + clk_i_bufg = Signal() + self.specials += [ + Instance("IBUFDS", + i_I=pads.clk_p, + i_IB=pads.clk_n, + o_O=clk_i + ) + ] + if use_bufr: + clk_i_bufr = Signal() + self.specials += [ + Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr), + Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg) + ] + else: + self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg) + self.comb += pll.refclk.eq(clk_i_bufg) + + # rx datapath + # serdes -> gearbox -> bitslip -> decoders -> rx_data + self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") + self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) + + self.submodules.phase_detector = ClockDomainsRenamer("serdes_5x")(PhaseDetector()) + + # 2 serdes for phase detection: 1 master (used for data) / 1 slave + serdes_m_i_nodelay = Signal() + serdes_s_i_nodelay = Signal() + self.specials += [ + Instance("IBUFDS_DIFF_OUT", + i_I=pads.rx_p, + i_IB=pads.rx_n, + o_O=serdes_m_i_nodelay, + o_OB=serdes_s_i_nodelay + ) + ] + + serdes_m_i_delayed = Signal() + serdes_m_q = Signal(8) + self.specials += [ + Instance("IDELAYE2", + p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", + p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", + p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE", + p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=0, + + i_C=ClockSignal(), + i_LD=self.rx_delay_rst, + i_CE=self.rx_delay_ce, + i_LDPIPEEN=0, i_INC=self.rx_delay_inc, + + i_IDATAIN=serdes_m_i_nodelay, o_DATAOUT=serdes_m_i_delayed + ), + Instance("ISERDESE2", + p_DATA_WIDTH=8, p_DATA_RATE="DDR", + p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", + p_NUM_CE=1, p_IOBDELAY="IFD", + + i_DDLY=serdes_m_i_delayed, + i_CE1=1, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKB=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + i_BITSLIP=0, + o_Q8=serdes_m_q[0], o_Q7=serdes_m_q[1], + o_Q6=serdes_m_q[2], o_Q5=serdes_m_q[3], + o_Q4=serdes_m_q[4], o_Q3=serdes_m_q[5], + o_Q2=serdes_m_q[6], o_Q1=serdes_m_q[7] + ) + ] + self.comb += self.phase_detector.mdata.eq(serdes_m_q) + + serdes_s_i_delayed = Signal() + serdes_s_q = Signal(8) + serdes_s_idelay_value = int(1/(4*pll.linerate)/78e-12) # 1/4 bit period + assert serdes_s_idelay_value < 32 + self.specials += [ + Instance("IDELAYE2", + p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", + p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", + p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE", + p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=serdes_s_idelay_value, + + i_C=ClockSignal(), + i_LD=self.rx_delay_rst, + i_CE=self.rx_delay_ce, + i_LDPIPEEN=0, i_INC=self.rx_delay_inc, + + i_IDATAIN=serdes_s_i_nodelay, o_DATAOUT=serdes_s_i_delayed + ), + Instance("ISERDESE2", + p_DATA_WIDTH=8, p_DATA_RATE="DDR", + p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", + p_NUM_CE=1, p_IOBDELAY="IFD", + + i_DDLY=serdes_s_i_delayed, + i_CE1=1, + i_RST=ResetSignal("serdes"), + i_CLK=ClockSignal("serdes_20x"), i_CLKB=~ClockSignal("serdes_20x"), + i_CLKDIV=ClockSignal("serdes_5x"), + i_BITSLIP=0, + o_Q8=serdes_s_q[0], o_Q7=serdes_s_q[1], + o_Q6=serdes_s_q[2], o_Q5=serdes_s_q[3], + o_Q4=serdes_s_q[4], o_Q3=serdes_s_q[5], + o_Q2=serdes_s_q[6], o_Q1=serdes_s_q[7] + ) + ] + self.comb += self.phase_detector.sdata.eq(~serdes_s_q) + + self.comb += [ + self.rx_gearbox.i.eq(serdes_m_q), + self.rx_bitslip.value.eq(rx_bitslip_value), + self.rx_bitslip.i.eq(self.rx_gearbox.o), + self.decoders[0].input.eq(self.rx_bitslip.o[0:10]), + self.decoders[1].input.eq(self.rx_bitslip.o[10:20]), + self.decoders[2].input.eq(self.rx_bitslip.o[20:30]), + self.decoders[3].input.eq(self.rx_bitslip.o[30:40]), + self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])), + rx_idle.eq(self.rx_bitslip.o == 0), + rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) & + ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) & + ((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0)) & + ((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0))) + + ] From da90a0fa12a7f6192ddee5355b515579e673aedf Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 21 Aug 2017 12:31:12 -0400 Subject: [PATCH 07/45] Add test for Etherbone Files copied directly from https://github.com/enjoy-digital/sayma_test @ 9ec62242659910ad1726beb00ff15b3f0a406615 --- artiq/gateware/test/serwb/__init__.py | 0 artiq/gateware/test/serwb/test_etherbone.py | 68 +++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 artiq/gateware/test/serwb/__init__.py create mode 100644 artiq/gateware/test/serwb/test_etherbone.py diff --git a/artiq/gateware/test/serwb/__init__.py b/artiq/gateware/test/serwb/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/artiq/gateware/test/serwb/test_etherbone.py b/artiq/gateware/test/serwb/test_etherbone.py new file mode 100644 index 000000000..df74b3ccf --- /dev/null +++ b/artiq/gateware/test/serwb/test_etherbone.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +from litex.gen import * + +import sys +sys.path.append("../../gateware/") + +from amc_rtm_link import packet +from amc_rtm_link import etherbone + +from litex.soc.interconnect.wishbone import SRAM +from litex.soc.interconnect.stream import Converter + + +class DUT(Module): + def __init__(self): + # wishbone slave + slave_depacketizer = packet.Depacketizer(int(100e6)) + slave_packetizer = packet.Packetizer() + self.submodules += slave_depacketizer, slave_packetizer + slave_etherbone = etherbone.Etherbone(mode="slave") + self.submodules += slave_etherbone + self.comb += [ + slave_depacketizer.source.connect(slave_etherbone.sink), + slave_etherbone.source.connect(slave_packetizer.sink) + ] + + # wishbone master + master_depacketizer = packet.Depacketizer(int(100e6)) + master_packetizer = packet.Packetizer() + self.submodules += master_depacketizer, master_packetizer + master_etherbone = etherbone.Etherbone(mode="master") + master_sram = SRAM(1024, bus=master_etherbone.wishbone.bus) + self.submodules += master_etherbone, master_sram + self.comb += [ + master_depacketizer.source.connect(master_etherbone.sink), + master_etherbone.source.connect(master_packetizer.sink) + ] + + # connect core directly with converters in the loop + s2m_downconverter = Converter(32, 16) + s2m_upconverter = Converter(16, 32) + self.submodules += s2m_downconverter, s2m_upconverter + m2s_downconverter = Converter(32, 16) + m2s_upconverter = Converter(16, 32) + self.submodules += m2s_upconverter, m2s_downconverter + self.comb += [ + slave_packetizer.source.connect(s2m_downconverter.sink), + s2m_downconverter.source.connect(s2m_upconverter.sink), + s2m_upconverter.source.connect(master_depacketizer.sink), + + master_packetizer.source.connect(m2s_downconverter.sink), + m2s_downconverter.source.connect(m2s_upconverter.sink), + m2s_upconverter.source.connect(slave_depacketizer.sink) + ] + + # expose wishbone slave + self.wishbone = slave_etherbone.wishbone.bus + +def main_generator(dut): + for i in range(8): + yield from dut.wishbone.write(0x100 + i, i) + for i in range(8): + data = (yield from dut.wishbone.read(0x100 + i)) + print("0x{:08x}".format(data)) + +dut = DUT() +run_simulation(dut, main_generator(dut), vcd_name="sim.vcd") From dac3a78b75447bc8054ecda9af5892cce33b6b0d Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 21 Aug 2017 12:35:59 -0400 Subject: [PATCH 08/45] serwb: style, use migen, fix imports --- artiq/gateware/serwb/etherbone.py | 2 +- artiq/gateware/serwb/kusphy.py | 2 +- artiq/gateware/serwb/s7phy.py | 2 +- artiq/gateware/test/serwb/test_etherbone.py | 22 ++++++++++----------- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/artiq/gateware/serwb/etherbone.py b/artiq/gateware/serwb/etherbone.py index 8337b4389..8add43044 100644 --- a/artiq/gateware/serwb/etherbone.py +++ b/artiq/gateware/serwb/etherbone.py @@ -15,7 +15,7 @@ from migen import * from misoc.interconnect import stream from misoc.interconnect import wishbone -from amc_rtm_link.packet import * +from artiq.gateware.serwb.packet import * class Packetizer(Module): diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py index ed3716491..4733fb58c 100644 --- a/artiq/gateware/serwb/kusphy.py +++ b/artiq/gateware/serwb/kusphy.py @@ -5,7 +5,7 @@ from migen.genlib.misc import BitSlip from misoc.cores.code_8b10b import Encoder, Decoder -from amc_rtm_link.phy import PhaseDetector +from artiq.gateware.serwb.phy import PhaseDetector class KUSSerdesPLL(Module): diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py index 861c38d05..ed1366805 100644 --- a/artiq/gateware/serwb/s7phy.py +++ b/artiq/gateware/serwb/s7phy.py @@ -5,7 +5,7 @@ from migen.genlib.misc import BitSlip from misoc.cores.code_8b10b import Encoder, Decoder -from amc_rtm_link.phy import PhaseDetector +from artiq.gateware.serwb.phy import PhaseDetector class S7SerdesPLL(Module): diff --git a/artiq/gateware/test/serwb/test_etherbone.py b/artiq/gateware/test/serwb/test_etherbone.py index df74b3ccf..248e3f68e 100644 --- a/artiq/gateware/test/serwb/test_etherbone.py +++ b/artiq/gateware/test/serwb/test_etherbone.py @@ -1,15 +1,10 @@ -#!/usr/bin/env python3 +from migen import * -from litex.gen import * +from misoc.interconnect.wishbone import SRAM +from misoc.interconnect.stream import Converter -import sys -sys.path.append("../../gateware/") - -from amc_rtm_link import packet -from amc_rtm_link import etherbone - -from litex.soc.interconnect.wishbone import SRAM -from litex.soc.interconnect.stream import Converter +from artiq.gateware.serwb import packet +from artiq.gateware.serwb import etherbone class DUT(Module): @@ -57,6 +52,7 @@ class DUT(Module): # expose wishbone slave self.wishbone = slave_etherbone.wishbone.bus + def main_generator(dut): for i in range(8): yield from dut.wishbone.write(0x100 + i, i) @@ -64,5 +60,7 @@ def main_generator(dut): data = (yield from dut.wishbone.read(0x100 + i)) print("0x{:08x}".format(data)) -dut = DUT() -run_simulation(dut, main_generator(dut), vcd_name="sim.vcd") + +if __name__ == "__main__": + dut = DUT() + run_simulation(dut, main_generator(dut), vcd_name="sim.vcd") From 53c7f92fdcddc14cbd95792561d83a649d7864d5 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 21 Aug 2017 15:57:43 -0400 Subject: [PATCH 09/45] serwb: add __init__.py and expose submodules --- artiq/gateware/serwb/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 artiq/gateware/serwb/__init__.py diff --git a/artiq/gateware/serwb/__init__.py b/artiq/gateware/serwb/__init__.py new file mode 100644 index 000000000..632008428 --- /dev/null +++ b/artiq/gateware/serwb/__init__.py @@ -0,0 +1 @@ +from artiq.gateware.serwb import s7phy, kusphy, phy, packet, etherbone From bfea29727933bc77565504b86ea672ad6c8ee378 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 21 Aug 2017 15:58:01 -0400 Subject: [PATCH 10/45] targets: add Sayma RTM --- artiq/gateware/targets/sayma_rtm.py | 135 ++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 artiq/gateware/targets/sayma_rtm.py diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py new file mode 100755 index 000000000..20d89404b --- /dev/null +++ b/artiq/gateware/targets/sayma_rtm.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.build.platforms.sinara import sayma_rtm + +from misoc.interconnect import wishbone, stream + +from artiq.gateware import serwb + + +class CRG(Module): + def __init__(self, platform): + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_clk200 = ClockDomain() + + clk50 = platform.request("clk50") + self.reset = Signal() + + pll_locked = Signal() + pll_fb = Signal() + pll_sys = Signal() + pll_clk200 = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1GHz + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=20.0, + p_CLKFBOUT_MULT=20, p_DIVCLK_DIVIDE=1, + i_CLKIN1=clk50, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, + + # 125MHz + p_CLKOUT0_DIVIDE=8, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_sys, + + # 200MHz + p_CLKOUT3_DIVIDE=5, p_CLKOUT3_PHASE=0.0, o_CLKOUT3=pll_clk200 + ), + Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), + Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk), + AsyncResetSynchronizer(self.cd_sys, ~pll_locked | self.reset), + AsyncResetSynchronizer(self.cd_clk200, ~pll_locked | self.reset) + ] + + reset_counter = Signal(4, reset=15) + ic_reset = Signal(reset=1) + self.sync.clk200 += \ + If(reset_counter != 0, + reset_counter.eq(reset_counter - 1) + ).Else( + ic_reset.eq(0) + ) + self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset) + + +class SaymaRTM(Module): + def __init__(self, platform): + self.submodules.crg = CRG(platform) + self.crg.cd_sys.clk.attr.add("keep") + clk_freq = 125e6 + platform.add_period_constraint(self.crg.cd_sys.clk, 8.0) + + sram = wishbone.SRAM(8192, init=[0x12345678, 0xbadc0f33, 0xabad1dea, 0xdeadbeef]) + self.submodules += sram + + # TODO: push all those serwb bits into library modules + # maybe keep only 3 user-visible modules: serwb PLL, serwb PHY, and serwb core + # TODO: after this is done, stop exposing internal modules in serwb/__init__.py + # TODO: avoid having a "serdes" clock domain at the top level, rename to "serwb_serdes" or similar. + + # serwb SERDES + serwb_pll = serwb.s7phy.S7SerdesPLL(125e6, 1.25e9, vco_div=1) + self.submodules += serwb_pll + + serwb_serdes = serwb.s7phy.S7Serdes(serwb_pll, platform.request("amc_rtm_serwb"), mode="slave") + self.submodules += serwb_serdes + serwb_init = serwb.phy.SerdesSlaveInit(serwb_serdes, taps=32) + self.submodules += serwb_init + serwb_control = serwb.phy.SerdesControl(serwb_init, mode="slave") + self.submodules += serwb_control + self.comb += self.crg.reset.eq(serwb_init.reset) + + serwb_serdes.cd_serdes.clk.attr.add("keep") + serwb_serdes.cd_serdes_20x.clk.attr.add("keep") + serwb_serdes.cd_serdes_5x.clk.attr.add("keep") + platform.add_period_constraint(serwb_serdes.cd_serdes.clk, 32.0), + platform.add_period_constraint(serwb_serdes.cd_serdes_20x.clk, 1.6), + platform.add_period_constraint(serwb_serdes.cd_serdes_5x.clk, 6.4) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, + serwb_serdes.cd_serdes.clk, + serwb_serdes.cd_serdes_5x.clk) + + # serwb master + serwb_depacketizer = serwb.packet.Depacketizer(int(clk_freq)) + serwb_packetizer = serwb.packet.Packetizer() + self.submodules += serwb_depacketizer, serwb_packetizer + serwb_etherbone = serwb.etherbone.Etherbone(mode="master") + self.submodules += serwb_etherbone + serwb_tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serdes"})( + stream.AsyncFIFO([("data", 32)], 8)) + self.submodules += serwb_tx_cdc + serwb_rx_cdc = ClockDomainsRenamer({"write": "serdes", "read": "sys"})( + stream.AsyncFIFO([("data", 32)], 8)) + self.submodules += serwb_rx_cdc + self.comb += [ + # core <--> etherbone + serwb_depacketizer.source.connect(serwb_etherbone.sink), + serwb_etherbone.source.connect(serwb_packetizer.sink), + + # core --> serdes + serwb_packetizer.source.connect(serwb_tx_cdc.sink), + If(serwb_tx_cdc.source.stb & serwb_init.ready, + serwb_serdes.tx_data.eq(serwb_tx_cdc.source.data) + ), + serwb_tx_cdc.source.ack.eq(serwb_init.ready), + + # serdes --> core + serwb_rx_cdc.sink.stb.eq(serwb_init.ready), + serwb_rx_cdc.sink.data.eq(serwb_serdes.rx_data), + serwb_rx_cdc.source.connect(serwb_depacketizer.sink), + ] + + # connect serwb to SRAM + self.comb += serwb_etherbone.wishbone.bus.connect(sram.bus) + + +def main(): + platform = sayma_rtm.Platform() + top = SaymaRTM(platform) + platform.build(top, build_dir="artiq_sayma_rtm") + + +if __name__ == "__main__": + main() From 1f2b373d0962f7663b7f025e6f46ec73c79178bc Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 21 Aug 2017 16:37:13 -0400 Subject: [PATCH 11/45] sayma_rtm: remove unnecessary serwb_control --- artiq/gateware/targets/sayma_rtm.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 20d89404b..e8482d6a7 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -76,8 +76,6 @@ class SaymaRTM(Module): self.submodules += serwb_serdes serwb_init = serwb.phy.SerdesSlaveInit(serwb_serdes, taps=32) self.submodules += serwb_init - serwb_control = serwb.phy.SerdesControl(serwb_init, mode="slave") - self.submodules += serwb_control self.comb += self.crg.reset.eq(serwb_init.reset) serwb_serdes.cd_serdes.clk.attr.add("keep") From 0459a70cf694bd5bdda0ce05993b2d07d43ebd5b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 21 Aug 2017 16:49:42 -0400 Subject: [PATCH 12/45] sayma_amc: cleanup, fix RTM UART forwarding --- artiq/gateware/targets/sayma_amc_standalone.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index d272a9cd9..ec0cc175d 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -35,22 +35,23 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): **kwargs) AMPSoC.__init__(self) platform = self.platform - platform.toolchain.bitstream_commands.extend([ - "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", - ]) + platform.toolchain.bitstream_commands.append( + "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]") self.submodules.leds = gpio.GPIOOut(Cat( platform.request("user_led", 0), platform.request("user_led", 1))) self.csr_devices.append("leds") + # forward RTM UART to second FTDI UART channel serial_1 = platform.request("serial", 1) serial_rtm = platform.request("serial_rtm") self.comb += [ serial_1.tx.eq(serial_rtm.rx), - serial_rtm.rx.eq(serial_1.tx) + serial_rtm.tx.eq(serial_1.rx) ] + # RTIO rtio_channels = [] for i in (2, 3): phy = ttl_simple.Output(platform.request("user_led", i)) @@ -91,6 +92,7 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): self.get_native_sdram_if()) self.csr_devices.append("rtio_analyzer") + def main(): parser = argparse.ArgumentParser( description="ARTIQ device binary builder / Sayma AMC stand-alone") From 668450db2649c5f8da578ac6c67eaf5fa96d7a27 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 21 Aug 2017 18:11:29 -0400 Subject: [PATCH 13/45] sayma_amc: add serwb --- .../gateware/targets/sayma_amc_standalone.py | 58 +++++++++++++++++++ artiq/gateware/targets/sayma_rtm.py | 1 + 2 files changed, 59 insertions(+) diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index ec0cc175d..b30e92853 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -7,9 +7,11 @@ from migen import * from misoc.cores import gpio from misoc.integration.soc_sdram import soc_sdram_args, soc_sdram_argdict from misoc.integration.builder import builder_args, builder_argdict +from misoc.interconnect import stream from misoc.targets.sayma_amc import MiniSoC from artiq.gateware.amp import AMPSoC, build_artiq_soc +from artiq.gateware import serwb from artiq.gateware import rtio from artiq.gateware.rtio.phy import ttl_simple from artiq import __version__ as artiq_version @@ -20,6 +22,7 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): "cri_con": 0x10000000, "rtio": 0x20000000, "rtio_dma": 0x30000000, + "serwb": 0x20000000, "mailbox": 0x70000000 } mem_map.update(MiniSoC.mem_map) @@ -51,6 +54,61 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): serial_rtm.tx.eq(serial_1.rx) ] + # AMC/RTM serwb + # TODO: cleanup (same comments as in sayma_rtm.py) + serwb_pll = serwb.kusphy.KUSSerdesPLL(self.clk_freq, 1.25e9, vco_div=2) + self.comb += serwb_pll.refclk.eq(self.crg.cd_sys.clk) + self.submodules += serwb_pll + + serwb_pads = platform.request("amc_rtm_serwb") + serwb_serdes = serwb.kusphy.KUSSerdes(serwb_pll, serwb_pads, mode="master") + self.submodules += serwb_serdes + serwb_init = serwb.phy.SerdesMasterInit(serwb_serdes, taps=512) + self.submodules += serwb_init + self.submodules.serwb_control = serwb.phy.SerdesControl(serwb_init, mode="master") + self.csr_devices.append("serwb_control") + + serwb_serdes.cd_serdes.clk.attr.add("keep") + serwb_serdes.cd_serdes_20x.clk.attr.add("keep") + serwb_serdes.cd_serdes_5x.clk.attr.add("keep") + platform.add_period_constraint(serwb_serdes.cd_serdes.clk, 32.0), + platform.add_period_constraint(serwb_serdes.cd_serdes_20x.clk, 1.6), + platform.add_period_constraint(serwb_serdes.cd_serdes_5x.clk, 6.4) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, + serwb_serdes.cd_serdes.clk, + serwb_serdes.cd_serdes_5x.clk) + + serwb_depacketizer = serwb.packet.Depacketizer(self.clk_freq) + serwb_packetizer = serwb.packet.Packetizer() + self.submodules += serwb_depacketizer, serwb_packetizer + serwb_etherbone = serwb.etherbone.Etherbone(mode="slave") + self.submodules += serwb_etherbone + serwb_tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serdes"})( + stream.AsyncFIFO([("data", 32)], 8)) + self.submodules += serwb_tx_cdc + serwb_rx_cdc = ClockDomainsRenamer({"write": "serdes", "read": "sys"})( + stream.AsyncFIFO([("data", 32)], 8)) + self.submodules += serwb_rx_cdc + self.comb += [ + # core <--> etherbone + serwb_depacketizer.source.connect(serwb_etherbone.sink), + serwb_etherbone.source.connect(serwb_packetizer.sink), + + # core --> serdes + serwb_packetizer.source.connect(serwb_tx_cdc.sink), + If(serwb_tx_cdc.source.stb & serwb_init.ready, + serwb_serdes.tx_data.eq(serwb_tx_cdc.source.data) + ), + serwb_tx_cdc.source.ack.eq(serwb_init.ready), + + # serdes --> core + serwb_rx_cdc.sink.stb.eq(serwb_init.ready), + serwb_rx_cdc.sink.data.eq(serwb_serdes.rx_data), + serwb_rx_cdc.source.connect(serwb_depacketizer.sink), + ] + self.add_wb_slave(self.mem_map["serwb"], 8192, serwb_etherbone.wishbone.bus) + # RTIO rtio_channels = [] for i in (2, 3): diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index e8482d6a7..56ac0ac1e 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -67,6 +67,7 @@ class SaymaRTM(Module): # maybe keep only 3 user-visible modules: serwb PLL, serwb PHY, and serwb core # TODO: after this is done, stop exposing internal modules in serwb/__init__.py # TODO: avoid having a "serdes" clock domain at the top level, rename to "serwb_serdes" or similar. + # TODO: the above also applies to sayma_amc_standalone.py. # serwb SERDES serwb_pll = serwb.s7phy.S7SerdesPLL(125e6, 1.25e9, vco_div=1) From 54c75d3274add6426ccadda468b2862ac18add54 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 23 Aug 2017 17:19:53 -0400 Subject: [PATCH 14/45] sayma_rtm: use CSR infrastructure, generate CSR CSV --- artiq/gateware/targets/sayma_rtm.py | 45 +++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 56ac0ac1e..da38e8787 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -1,10 +1,15 @@ #!/usr/bin/env python3 +import os + from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer from migen.build.platforms.sinara import sayma_rtm from misoc.interconnect import wishbone, stream +from misoc.interconnect.csr import * +from misoc.integration.wb_slaves import WishboneSlaveManager +from misoc.integration.cpu_interface import get_csr_csv from artiq.gateware import serwb @@ -53,15 +58,27 @@ class CRG(Module): self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset) +class RTMIdentifier(Module, AutoCSR): + def __init__(self): + self.identifier = CSRStatus(32) + self.comb += self.identifier.status.eq(0x5352544d) # "SRTM" + + +CSR_RANGE_SIZE = 0x800 + + class SaymaRTM(Module): + def __init__(self, platform): + csr_devices = [] + self.submodules.crg = CRG(platform) self.crg.cd_sys.clk.attr.add("keep") clk_freq = 125e6 platform.add_period_constraint(self.crg.cd_sys.clk, 8.0) - sram = wishbone.SRAM(8192, init=[0x12345678, 0xbadc0f33, 0xabad1dea, 0xdeadbeef]) - self.submodules += sram + self.submodules.rtm_identifier = RTMIdentifier() + csr_devices.append("rtm_identifier") # TODO: push all those serwb bits into library modules # maybe keep only 3 user-visible modules: serwb PLL, serwb PHY, and serwb core @@ -120,14 +137,32 @@ class SaymaRTM(Module): serwb_rx_cdc.source.connect(serwb_depacketizer.sink), ] - # connect serwb to SRAM - self.comb += serwb_etherbone.wishbone.bus.connect(sram.bus) + # process CSR devices and connect them to serwb + self.csr_regions = [] + wb_slaves = WishboneSlaveManager(0x10000000) + for i, name in enumerate(csr_devices): + origin = i*CSR_RANGE_SIZE + module = getattr(self, name) + csrs = module.get_csrs() + + bank = wishbone.CSRBank(csrs) + self.submodules += bank + + wb_slaves.add(origin, CSR_RANGE_SIZE, bank.bus) + self.csr_regions.append((name, origin, 32, csrs)) + + self.submodules += wishbone.Decoder(serwb_etherbone.wishbone.bus, + wb_slaves.get_interconnect_slaves(), + register=True) def main(): + build_dir = "artiq_sayma_rtm" platform = sayma_rtm.Platform() top = SaymaRTM(platform) - platform.build(top, build_dir="artiq_sayma_rtm") + with open(os.path.join(build_dir, "sayma_rtm_csr.csv"), "w") as f: + f.write(get_csr_csv(top.csr_regions)) + platform.build(top, build_dir=build_dir) if __name__ == "__main__": From dbc12540dae24f5ca4a37af6f4cbdaeeb115eace Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 26 Aug 2017 14:48:11 -0700 Subject: [PATCH 15/45] sayma_amc: register RTM CSR regions from CSV --- artiq/gateware/remote_csr.py | 40 +++++++++++++++++++ .../gateware/targets/sayma_amc_standalone.py | 18 +++++++-- 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 artiq/gateware/remote_csr.py diff --git a/artiq/gateware/remote_csr.py b/artiq/gateware/remote_csr.py new file mode 100644 index 000000000..32644b8f9 --- /dev/null +++ b/artiq/gateware/remote_csr.py @@ -0,0 +1,40 @@ +from collections import OrderedDict +from operator import itemgetter +import csv + +from misoc.interconnect.csr import CSRStatus, CSRStorage + + +def _get_csr_data(csv_file): + csr_data = OrderedDict() + with open(csv_file) as csv_file_f: + csv_reader = csv.reader(csv_file_f) + for name, address, length, ro in csv_reader: + region_name, csr_name = name.split(".") + address = int(address, 0) + length = int(length, 0) + ro = ro == "ro" + if region_name not in csr_data: + csr_data[region_name] = [] + csr_data[region_name].append((csr_name, address, length, ro)) + return csr_data + + +def get_remote_csr_regions(offset, csv_file): + regions = [] + for region_name, csrs_info in _get_csr_data(csv_file).items(): + csrs_info = sorted(csrs_info, key=itemgetter(1)) + origin = csrs_info[0][1] + next_address = origin + csrs = [] + for csr_name, address, length, ro in csrs_info: + if address != next_address: + raise ValueError("CSRs are not contiguous") + next_address += 4*length + if ro: + csr = CSRStatus(32*length, name=csr_name) + else: + csr = CSRStorage(32*length, name=csr_name) + csrs.append(csr) + regions.append((region_name, offset + origin, 32, csrs)) + return regions diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index b30e92853..836db203e 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse +import os from migen import * @@ -12,6 +13,7 @@ from misoc.targets.sayma_amc import MiniSoC from artiq.gateware.amp import AMPSoC, build_artiq_soc from artiq.gateware import serwb +from artiq.gateware import remote_csr from artiq.gateware import rtio from artiq.gateware.rtio.phy import ttl_simple from artiq import __version__ as artiq_version @@ -20,9 +22,9 @@ from artiq import __version__ as artiq_version class SaymaAMCStandalone(MiniSoC, AMPSoC): mem_map = { "cri_con": 0x10000000, - "rtio": 0x20000000, - "rtio_dma": 0x30000000, - "serwb": 0x20000000, + "rtio": 0x11000000, + "rtio_dma": 0x12000000, + "serwb": 0x13000000, "mailbox": 0x70000000 } mem_map.update(MiniSoC.mem_map) @@ -156,9 +158,19 @@ def main(): description="ARTIQ device binary builder / Sayma AMC stand-alone") builder_args(parser) soc_sdram_args(parser) + parser.add_argument("--rtm-csr-csv", + default=os.path.join("artiq_sayma_rtm", "sayma_rtm_csr.csv"), + help="CSV file listing remote CSRs on RTM (default: %(default)s)") args = parser.parse_args() soc = SaymaAMCStandalone(**soc_sdram_argdict(args)) + + remote_csr_regions = remote_csr.get_remote_csr_regions( + soc.mem_map["serwb"] | soc.shadow_base, + args.rtm_csr_csv) + for name, origin, busword, csrs in remote_csr_regions: + soc.add_csr_region(name, origin, busword, csrs) + build_artiq_soc(soc, builder_argdict(args)) From 9194402ea5eb5f3c249cb37f3384a2e9f6d7318f Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 26 Aug 2017 16:31:31 -0700 Subject: [PATCH 16/45] sayma_rtm: expose HMC SPI bus --- artiq/gateware/targets/sayma_rtm.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index da38e8787..c9f1a0001 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -8,6 +8,7 @@ from migen.build.platforms.sinara import sayma_rtm from misoc.interconnect import wishbone, stream from misoc.interconnect.csr import * +from misoc.cores import spi from misoc.integration.wb_slaves import WishboneSlaveManager from misoc.integration.cpu_interface import get_csr_csv @@ -80,6 +81,10 @@ class SaymaRTM(Module): self.submodules.rtm_identifier = RTMIdentifier() csr_devices.append("rtm_identifier") + self.submodules.converter_spi = spi.SPIMaster(platform.request("hmc_spi")) + csr_devices.append("converter_spi") + self.comb += platform.request("hmc7043_reset").eq(0) + # TODO: push all those serwb bits into library modules # maybe keep only 3 user-visible modules: serwb PLL, serwb PHY, and serwb core # TODO: after this is done, stop exposing internal modules in serwb/__init__.py From d609c67cbd7fc9cd0b2528502c8a92f762dec021 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 26 Aug 2017 16:48:10 -0700 Subject: [PATCH 17/45] sayma_rtm: set clock mux pins --- artiq/gateware/targets/sayma_rtm.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index c9f1a0001..fd4d93ab5 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -81,6 +81,13 @@ class SaymaRTM(Module): self.submodules.rtm_identifier = RTMIdentifier() csr_devices.append("rtm_identifier") + # clock mux: 125MHz ext SMA clock to HMC830 input + self.comb += [ + platform.request("clk_src_ext_sel").eq(1), # use ext clk from sma + platform.request("ref_clk_src_sel").eq(1), + platform.request("dac_clk_src_sel").eq(0), # use clk from dac_clk + ] + self.submodules.converter_spi = spi.SPIMaster(platform.request("hmc_spi")) csr_devices.append("converter_spi") self.comb += platform.request("hmc7043_reset").eq(0) From 26a11a296cdb763d951b4d38d7822f0a1b7fbf1b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 26 Aug 2017 16:57:02 -0700 Subject: [PATCH 18/45] sayma_rtm: drive DAC control signals --- artiq/gateware/targets/sayma_rtm.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index fd4d93ab5..ef378b484 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -88,6 +88,12 @@ class SaymaRTM(Module): platform.request("dac_clk_src_sel").eq(0), # use clk from dac_clk ] + self.comb += [ + platform.request("ad9154_rst_n").eq(0), + platform.request("ad9154_txen", 0).eq(0b11), + platform.request("ad9154_txen", 1).eq(0b11) + ] + self.submodules.converter_spi = spi.SPIMaster(platform.request("hmc_spi")) csr_devices.append("converter_spi") self.comb += platform.request("hmc7043_reset").eq(0) From 89558e265361682ed44a9c66cadc47dd37b97007 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 29 Aug 2017 13:38:52 +0200 Subject: [PATCH 19/45] gateware/serwb: for the initial version set delay in the center of the valid sampling window and don't use phase detectors we'll use phase detectors later when it will be working reliably for both artix7 and kintex ultrascale --- artiq/gateware/serwb/kusphy.py | 56 +----- artiq/gateware/serwb/phy.py | 327 ++++++++++++++------------------- artiq/gateware/serwb/s7phy.py | 67 ++----- 3 files changed, 158 insertions(+), 292 deletions(-) diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py index 4733fb58c..412f469e4 100644 --- a/artiq/gateware/serwb/kusphy.py +++ b/artiq/gateware/serwb/kusphy.py @@ -5,8 +5,6 @@ from migen.genlib.misc import BitSlip from misoc.cores.code_8b10b import Encoder, Decoder -from artiq.gateware.serwb.phy import PhaseDetector - class KUSSerdesPLL(Module): def __init__(self, refclk_freq, linerate, vco_div=1): @@ -228,22 +226,17 @@ class KUSSerdes(Module): self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) - self.submodules.phase_detector = ClockDomainsRenamer("serdes_5x")(PhaseDetector()) - - # 2 serdes for phase detection: 1 master (used for data) / 1 slave - serdes_m_i_nodelay = Signal() - serdes_s_i_nodelay = Signal() + serdes_i_nodelay = Signal() self.specials += [ Instance("IBUFDS_DIFF_OUT", i_I=pads.rx_p, i_IB=pads.rx_n, - o_O=serdes_m_i_nodelay, - o_OB=serdes_s_i_nodelay + o_O=serdes_i_nodelay ) ] - serdes_m_i_delayed = Signal() - serdes_m_q = Signal(8) + serdes_i_delayed = Signal() + serdes_q = Signal(8) self.specials += [ Instance("IDELAYE3", p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0, @@ -256,55 +249,22 @@ class KUSSerdes(Module): i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, i_CE=rx_delay_ce, - i_IDATAIN=serdes_m_i_nodelay, o_DATAOUT=serdes_m_i_delayed + i_IDATAIN=serdes_i_nodelay, o_DATAOUT=serdes_i_delayed ), Instance("ISERDESE3", p_DATA_WIDTH=8, - i_D=serdes_m_i_delayed, + i_D=serdes_i_delayed, i_RST=ResetSignal("serdes"), i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, i_CLK=ClockSignal("serdes_20x"), i_CLK_B=~ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), - o_Q=serdes_m_q + o_Q=serdes_q ) ] - self.comb += self.phase_detector.mdata.eq(serdes_m_q) - - serdes_s_i_delayed = Signal() - serdes_s_q = Signal(8) - self.specials += [ - Instance("IDELAYE3", - p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0, - p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0, - # Note: can't use TIME mode since not reloading DELAY_VALUE on rst... - # Got answer from Xilinx, need testing: - # https://forums.xilinx.com/xlnx/board/crawl_message?board.id=ultrascale&message.id=4699 - p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN", - p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=50, # 1/4 bit period (ambient temp) - - i_CLK=ClockSignal("serdes_5x"), - i_RST=rx_delay_rst, i_LOAD=0, - i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, - i_CE=rx_delay_ce, - - i_IDATAIN=serdes_s_i_nodelay, o_DATAOUT=serdes_s_i_delayed - ), - Instance("ISERDESE3", - p_DATA_WIDTH=8, - - i_D=serdes_s_i_delayed, - i_RST=ResetSignal("serdes"), - i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, - i_CLK=ClockSignal("serdes_20x"), i_CLK_B=~ClockSignal("serdes_20x"), - i_CLKDIV=ClockSignal("serdes_5x"), - o_Q=serdes_s_q - ) - ] - self.comb += self.phase_detector.sdata.eq(~serdes_s_q) self.comb += [ - self.rx_gearbox.i.eq(serdes_m_q), + self.rx_gearbox.i.eq(serdes_q), self.rx_bitslip.value.eq(rx_bitslip_value), self.rx_bitslip.i.eq(self.rx_gearbox.o), self.decoders[0].input.eq(self.rx_bitslip.o[0:10]), diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py index 39640208d..179e141f9 100644 --- a/artiq/gateware/serwb/phy.py +++ b/artiq/gateware/serwb/phy.py @@ -5,86 +5,6 @@ from migen.genlib.misc import WaitTimer from misoc.interconnect.csr import * -class PhaseDetector(Module, AutoCSR): - def __init__(self, nbits=8): - self.mdata = Signal(8) - self.sdata = Signal(8) - - self.reset = Signal() - self.too_early = Signal() - self.too_late = Signal() - - # # # - - # Ideal sampling (middle of the eye): - # _____ _____ _____ - # | |_____| |_____| |_____| data - # + + + + + + master sampling - # - - - - - - slave sampling (90°/bit period) - # Since taps are fixed length delays, this ideal case is not possible - # and we will fall in the 2 following possible cases: - # - # 1) too late sampling (idelay needs to be decremented): - # _____ _____ _____ - # | |_____| |_____| |_____| data - # + + + + + + master sampling - # - - - - - - slave sampling (90°/bit period) - # On mdata transition, mdata != sdata - # - # - # 2) too early sampling (idelay needs to be incremented): - # _____ _____ _____ - # | |_____| |_____| |_____| data - # + + + + + + master sampling - # - - - - - - slave sampling (90°/bit period) - # On mdata transition, mdata == sdata - - transition = Signal() - inc = Signal() - dec = Signal() - - # Find transition - mdata_d = Signal(8) - self.sync.serdes_5x += mdata_d.eq(self.mdata) - self.comb += transition.eq(mdata_d != self.mdata) - - - # Find what to do - self.comb += [ - inc.eq(transition & (self.mdata == self.sdata)), - dec.eq(transition & (self.mdata != self.sdata)) - ] - - # Error accumulator - lateness = Signal(nbits, reset=2**(nbits - 1)) - too_late = Signal() - too_early = Signal() - reset_lateness = Signal() - self.comb += [ - too_late.eq(lateness == (2**nbits - 1)), - too_early.eq(lateness == 0) - ] - self.sync.serdes_5x += [ - If(reset_lateness, - lateness.eq(2**(nbits - 1)) - ).Elif(~too_late & ~too_early, - If(inc, lateness.eq(lateness - 1)), - If(dec, lateness.eq(lateness + 1)) - ) - ] - - # control / status cdc - self.specials += [ - MultiReg(too_early, self.too_early), - MultiReg(too_late, self.too_late) - ] - self.submodules.do_reset_lateness = PulseSynchronizer("sys", "serdes_5x") - self.comb += [ - reset_lateness.eq(self.do_reset_lateness.o), - self.do_reset_lateness.i.eq(self.reset) - ] - - # Master <--> Slave synchronization: # 1) Master sends idle pattern (zeroes) to reset Slave. # 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle pattern. @@ -102,25 +22,26 @@ class SerdesMasterInit(Module): # # # self.delay = delay = Signal(max=taps) - self.delay_found = delay_found = Signal() + self.delay_min = delay_min = Signal(max=taps) + self.delay_min_found = delay_min_found = Signal() + self.delay_max = delay_max = Signal(max=taps) + self.delay_max_found = delay_max_found = Signal() self.bitslip = bitslip = Signal(max=40) - self.bitslip_found = bitslip_found = Signal() - timer = WaitTimer(8192) + timer = WaitTimer(1024) self.submodules += timer self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) self.comb += self.fsm.reset.eq(self.reset) - phase_detector_too_early_last = Signal() - fsm.act("IDLE", - NextValue(delay_found, 0), NextValue(delay, 0), + NextValue(delay_min, 0), + NextValue(delay_min_found, 0), + NextValue(delay_max, 0), + NextValue(delay_max_found, 0), serdes.rx_delay_rst.eq(1), - NextValue(bitslip_found, 0), NextValue(bitslip, 0), - NextValue(phase_detector_too_early_last, 0), NextState("RESET_SLAVE"), serdes.tx_idle.eq(1) ) @@ -142,61 +63,75 @@ class SerdesMasterInit(Module): timer.wait.eq(1), If(timer.done, timer.wait.eq(0), - serdes.phase_detector.reset.eq(1), - If(~delay_found, - NextState("CHECK_PHASE") - ).Else( - NextState("CHECK_PATTERN") - ), - ), - serdes.tx_comma.eq(1) - ) - fsm.act("CHECK_PHASE", - # Since we are always incrementing delay, - # ideal sampling is found when phase detector - # transitions from too_early to too_late - If(serdes.phase_detector.too_late & - phase_detector_too_early_last, - NextValue(delay_found, 1), NextState("CHECK_PATTERN") - ).Elif(serdes.phase_detector.too_late | - serdes.phase_detector.too_early, - NextValue(phase_detector_too_early_last, - serdes.phase_detector.too_early), - NextState("INC_DELAY") - ), - serdes.tx_comma.eq(1) - ) - fsm.act("INC_DELAY", - If(delay == (taps - 1), - NextState("ERROR") - ).Else( - NextValue(delay, delay + 1), - serdes.rx_delay_inc.eq(1), - serdes.rx_delay_ce.eq(1), - NextState("WAIT_STABLE") ), serdes.tx_comma.eq(1) ) fsm.act("CHECK_PATTERN", - If(serdes.rx_comma, - timer.wait.eq(1), - If(timer.done, - NextValue(bitslip_found, 1), - NextState("READY") - ) + If(~delay_min_found, + If(serdes.rx_comma, + timer.wait.eq(1), + If(timer.done, + NextValue(delay_min, delay), + NextValue(delay_min_found, 1) + ) + ).Else( + NextState("INC_DELAY_BITSLIP") + ), ).Else( - NextState("INC_BITSLIP") + If(~serdes.rx_comma, + NextValue(delay_max, delay), + NextValue(delay_max_found, 1), + NextState("RESET_SAMPLING_WINDOW") + ).Else( + NextState("INC_DELAY_BITSLIP") + ) ), serdes.tx_comma.eq(1) ) self.comb += serdes.rx_bitslip_value.eq(bitslip) - fsm.act("INC_BITSLIP", - If(bitslip == (40 - 1), - NextState("ERROR") + fsm.act("INC_DELAY_BITSLIP", + NextState("WAIT_STABLE"), + If(delay == (taps - 1), + If(delay_min_found, + NextState("ERROR") + ), + If(bitslip == (40 - 1), + NextValue(bitslip, 0) + ).Else( + NextValue(bitslip, bitslip + 1) + ), + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1) ).Else( - NextValue(bitslip, bitslip + 1), - NextState("WAIT_STABLE") + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1) + ), + serdes.tx_comma.eq(1) + ) + fsm.act("RESET_SAMPLING_WINDOW", + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1), + NextState("WAIT_SAMPLING_WINDOW"), + serdes.tx_comma.eq(1) + ) + fsm.act("CONFIGURE_SAMPLING_WINDOW", + If(delay == (delay_min + (delay_max - delay_min)[1:]), + NextState("READY") + ).Else( + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1), + NextState("WAIT_SAMPLING_WINDOW") + ), + serdes.tx_comma.eq(1) + ) + fsm.act("WAIT_SAMPLING_WINDOW", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + NextState("CONFIGURE_SAMPLING_WINDOW") ), serdes.tx_comma.eq(1) ) @@ -217,9 +152,11 @@ class SerdesSlaveInit(Module, AutoCSR): # # # self.delay = delay = Signal(max=taps) - self.delay_found = delay_found = Signal() + self.delay_min = delay_min = Signal(max=taps) + self.delay_min_found = delay_min_found = Signal() + self.delay_max = delay_max = Signal(max=taps) + self.delay_max_found = delay_max_found = Signal() self.bitslip = bitslip = Signal(max=40) - self.bitslip_found = bitslip_found = Signal() timer = WaitTimer(1024) self.submodules += timer @@ -227,16 +164,14 @@ class SerdesSlaveInit(Module, AutoCSR): self.comb += self.reset.eq(serdes.rx_idle) self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) - - phase_detector_too_early_last = Signal() - fsm.act("IDLE", - NextValue(delay_found, 0), NextValue(delay, 0), + NextValue(delay_min, 0), + NextValue(delay_min_found, 0), + NextValue(delay_max, 0), + NextValue(delay_max_found, 0), serdes.rx_delay_rst.eq(1), - NextValue(bitslip_found, 0), NextValue(bitslip, 0), - NextValue(phase_detector_too_early_last, 0), NextState("WAIT_STABLE"), serdes.tx_idle.eq(1) ) @@ -244,64 +179,76 @@ class SerdesSlaveInit(Module, AutoCSR): timer.wait.eq(1), If(timer.done, timer.wait.eq(0), - serdes.phase_detector.reset.eq(1), - If(~delay_found, - NextState("CHECK_PHASE") - ).Else( - NextState("CHECK_PATTERN") - ), - ), - serdes.tx_idle.eq(1) - ) - fsm.act("CHECK_PHASE", - # Since we are always incrementing delay, - # ideal sampling is found when phase detector - # transitions from too_early to too_late - If(serdes.phase_detector.too_late & - phase_detector_too_early_last, - NextValue(delay_found, 1), NextState("CHECK_PATTERN") - ).Elif(serdes.phase_detector.too_late | - serdes.phase_detector.too_early, - NextValue(phase_detector_too_early_last, - serdes.phase_detector.too_early), - NextState("INC_DELAY") - ), - serdes.tx_idle.eq(1) - ) - fsm.act("INC_DELAY", - If(delay == (taps - 1), - NextState("ERROR") - ).Else( - NextValue(delay, delay + 1), - serdes.rx_delay_inc.eq(1), - serdes.rx_delay_ce.eq(1), - NextState("WAIT_STABLE") ), serdes.tx_idle.eq(1) ) fsm.act("CHECK_PATTERN", - If(serdes.rx_comma, - timer.wait.eq(1), - If(timer.done, - NextValue(bitslip_found, 1), - NextState("SEND_PATTERN") - ) + If(~delay_min_found, + If(serdes.rx_comma, + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + NextValue(delay_min, delay), + NextValue(delay_min_found, 1) + ) + ).Else( + NextState("INC_DELAY_BITSLIP") + ), ).Else( - NextState("INC_BITSLIP") + If(~serdes.rx_comma, + NextValue(delay_max, delay), + NextValue(delay_max_found, 1), + NextState("RESET_SAMPLING_WINDOW") + ).Else( + NextState("INC_DELAY_BITSLIP") + ) ), serdes.tx_idle.eq(1) ) self.comb += serdes.rx_bitslip_value.eq(bitslip) - fsm.act("INC_BITSLIP", - If(bitslip == (40 - 1), - NextState("ERROR") + fsm.act("INC_DELAY_BITSLIP", + NextState("WAIT_STABLE"), + If(delay == (taps - 1), + If(delay_min_found, + NextState("ERROR") + ), + If(bitslip == (40 - 1), + NextValue(bitslip, 0) + ).Else( + NextValue(bitslip, bitslip + 1) + ), + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1) ).Else( - NextValue(bitslip, bitslip + 1), - NextState("WAIT_STABLE") + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1) ), serdes.tx_idle.eq(1) ) + fsm.act("RESET_SAMPLING_WINDOW", + NextValue(delay, 0), + serdes.rx_delay_rst.eq(1), + NextState("WAIT_SAMPLING_WINDOW") + ) + fsm.act("CONFIGURE_SAMPLING_WINDOW", + If(delay == (delay_min + (delay_max - delay_min)[1:]), + NextState("SEND_PATTERN") + ).Else( + NextValue(delay, delay + 1), + serdes.rx_delay_inc.eq(1), + serdes.rx_delay_ce.eq(1), + NextState("WAIT_SAMPLING_WINDOW") + ) + ) + fsm.act("WAIT_SAMPLING_WINDOW", + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + NextState("CONFIGURE_SAMPLING_WINDOW") + ) + ) fsm.act("SEND_PATTERN", timer.wait.eq(1), If(timer.done, @@ -327,9 +274,11 @@ class SerdesControl(Module, AutoCSR): self.error = CSRStatus() self.delay = CSRStatus(9) - self.delay_found = CSRStatus() + self.delay_min_found = CSRStatus() + self.delay_min = CSRStatus(9) + self.delay_max_found = CSRStatus() + self.delay_max = CSRStatus(9) self.bitslip = CSRStatus(6) - self.bitslip_found = CSRStatus() # # # @@ -338,8 +287,10 @@ class SerdesControl(Module, AutoCSR): self.comb += [ self.ready.status.eq(init.ready), self.error.status.eq(init.error), - self.delay_found.status.eq(init.delay_found), self.delay.status.eq(init.delay), - self.bitslip_found.status.eq(init.bitslip_found), + self.delay_min_found.status.eq(init.delay_min_found), + self.delay_min.status.eq(init.delay_min), + self.delay_max_found.status.eq(init.delay_max_found), + self.delay_max.status.eq(init.delay_max), self.bitslip.status.eq(init.bitslip) ] diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py index ed1366805..2b5a0473c 100644 --- a/artiq/gateware/serwb/s7phy.py +++ b/artiq/gateware/serwb/s7phy.py @@ -5,8 +5,6 @@ from migen.genlib.misc import BitSlip from misoc.cores.code_8b10b import Encoder, Decoder -from artiq.gateware.serwb.phy import PhaseDetector - class S7SerdesPLL(Module): def __init__(self, refclk_freq, linerate, vco_div=1): @@ -221,22 +219,17 @@ class S7Serdes(Module): self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) - self.submodules.phase_detector = ClockDomainsRenamer("serdes_5x")(PhaseDetector()) - - # 2 serdes for phase detection: 1 master (used for data) / 1 slave - serdes_m_i_nodelay = Signal() - serdes_s_i_nodelay = Signal() + serdes_i_nodelay = Signal() self.specials += [ Instance("IBUFDS_DIFF_OUT", i_I=pads.rx_p, i_IB=pads.rx_n, - o_O=serdes_m_i_nodelay, - o_OB=serdes_s_i_nodelay + o_O=serdes_i_nodelay ) ] - serdes_m_i_delayed = Signal() - serdes_m_q = Signal(8) + serdes_i_delayed = Signal() + serdes_q = Signal(8) self.specials += [ Instance("IDELAYE2", p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", @@ -249,66 +242,28 @@ class S7Serdes(Module): i_CE=self.rx_delay_ce, i_LDPIPEEN=0, i_INC=self.rx_delay_inc, - i_IDATAIN=serdes_m_i_nodelay, o_DATAOUT=serdes_m_i_delayed + i_IDATAIN=serdes_i_nodelay, o_DATAOUT=serdes_i_delayed ), Instance("ISERDESE2", p_DATA_WIDTH=8, p_DATA_RATE="DDR", p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1, p_IOBDELAY="IFD", - i_DDLY=serdes_m_i_delayed, + i_DDLY=serdes_i_delayed, i_CE1=1, i_RST=ResetSignal("serdes"), i_CLK=ClockSignal("serdes_20x"), i_CLKB=~ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), i_BITSLIP=0, - o_Q8=serdes_m_q[0], o_Q7=serdes_m_q[1], - o_Q6=serdes_m_q[2], o_Q5=serdes_m_q[3], - o_Q4=serdes_m_q[4], o_Q3=serdes_m_q[5], - o_Q2=serdes_m_q[6], o_Q1=serdes_m_q[7] + o_Q8=serdes_q[0], o_Q7=serdes_q[1], + o_Q6=serdes_q[2], o_Q5=serdes_q[3], + o_Q4=serdes_q[4], o_Q3=serdes_q[5], + o_Q2=serdes_q[6], o_Q1=serdes_q[7] ) ] - self.comb += self.phase_detector.mdata.eq(serdes_m_q) - - serdes_s_i_delayed = Signal() - serdes_s_q = Signal(8) - serdes_s_idelay_value = int(1/(4*pll.linerate)/78e-12) # 1/4 bit period - assert serdes_s_idelay_value < 32 - self.specials += [ - Instance("IDELAYE2", - p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", - p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", - p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE", - p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=serdes_s_idelay_value, - - i_C=ClockSignal(), - i_LD=self.rx_delay_rst, - i_CE=self.rx_delay_ce, - i_LDPIPEEN=0, i_INC=self.rx_delay_inc, - - i_IDATAIN=serdes_s_i_nodelay, o_DATAOUT=serdes_s_i_delayed - ), - Instance("ISERDESE2", - p_DATA_WIDTH=8, p_DATA_RATE="DDR", - p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", - p_NUM_CE=1, p_IOBDELAY="IFD", - - i_DDLY=serdes_s_i_delayed, - i_CE1=1, - i_RST=ResetSignal("serdes"), - i_CLK=ClockSignal("serdes_20x"), i_CLKB=~ClockSignal("serdes_20x"), - i_CLKDIV=ClockSignal("serdes_5x"), - i_BITSLIP=0, - o_Q8=serdes_s_q[0], o_Q7=serdes_s_q[1], - o_Q6=serdes_s_q[2], o_Q5=serdes_s_q[3], - o_Q4=serdes_s_q[4], o_Q3=serdes_s_q[5], - o_Q2=serdes_s_q[6], o_Q1=serdes_s_q[7] - ) - ] - self.comb += self.phase_detector.sdata.eq(~serdes_s_q) self.comb += [ - self.rx_gearbox.i.eq(serdes_m_q), + self.rx_gearbox.i.eq(serdes_q), self.rx_bitslip.value.eq(rx_bitslip_value), self.rx_bitslip.i.eq(self.rx_gearbox.o), self.decoders[0].input.eq(self.rx_bitslip.o[0:10]), From 60ad36e7d60216084e5f15d1c0288b7afd5ce46b Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 29 Aug 2017 13:43:26 +0200 Subject: [PATCH 20/45] gateware/serwb: generate wishbone error on wishbone slave when access while link is not ready --- artiq/gateware/serwb/etherbone.py | 16 ++++++++++++---- artiq/gateware/targets/sayma_amc_standalone.py | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/serwb/etherbone.py b/artiq/gateware/serwb/etherbone.py index 8add43044..a5dff54c0 100644 --- a/artiq/gateware/serwb/etherbone.py +++ b/artiq/gateware/serwb/etherbone.py @@ -645,6 +645,7 @@ class EtherboneWishboneMaster(Module): class EtherboneWishboneSlave(Module): def __init__(self): self.bus = bus = wishbone.Interface() + self.ready = Signal(reset=1) self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) self.source = source = stream.Endpoint(etherbone_mmap_description(32)) @@ -654,10 +655,14 @@ class EtherboneWishboneSlave(Module): fsm.act("IDLE", sink.ack.eq(1), If(bus.stb & bus.cyc, - If(bus.we, - NextState("SEND_WRITE") + If(self.ready, + If(bus.we, + NextState("SEND_WRITE") + ).Else( + NextState("SEND_READ") + ) ).Else( - NextState("SEND_READ") + NextState("SEND_ERROR") ) ) ) @@ -694,7 +699,10 @@ class EtherboneWishboneSlave(Module): NextState("IDLE") ) ) - + fsm.act("SEND_ERROR", + bus.ack.eq(1), + bus.err.eq(1) + ) # etherbone diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index 836db203e..554964b74 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -86,6 +86,7 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): self.submodules += serwb_depacketizer, serwb_packetizer serwb_etherbone = serwb.etherbone.Etherbone(mode="slave") self.submodules += serwb_etherbone + self.comb += serwb_etherbone.wishbone.ready.eq(serwb_init.ready) serwb_tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serdes"})( stream.AsyncFIFO([("data", 32)], 8)) self.submodules += serwb_tx_cdc From 7d7f6be7ceb7f568b1bc004b62465b820af75d78 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 29 Aug 2017 16:41:29 +0200 Subject: [PATCH 21/45] gateware/serwb: generate wishbone error if link loose ready in the middle of a transaction --- artiq/gateware/serwb/etherbone.py | 50 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/artiq/gateware/serwb/etherbone.py b/artiq/gateware/serwb/etherbone.py index a5dff54c0..9ac7bdb7e 100644 --- a/artiq/gateware/serwb/etherbone.py +++ b/artiq/gateware/serwb/etherbone.py @@ -667,33 +667,43 @@ class EtherboneWishboneSlave(Module): ) ) fsm.act("SEND_WRITE", - source.stb.eq(1), - source.eop.eq(1), - source.base_addr[2:].eq(bus.adr), - source.count.eq(1), - source.be.eq(bus.sel), - source.we.eq(1), - source.data.eq(bus.dat_w), - If(source.stb & source.ack, - bus.ack.eq(1), - NextState("IDLE") + If(~self.ready, + NextState("SEND_ERROR") + ).Else( + source.stb.eq(1), + source.eop.eq(1), + source.base_addr[2:].eq(bus.adr), + source.count.eq(1), + source.be.eq(bus.sel), + source.we.eq(1), + source.data.eq(bus.dat_w), + If(source.stb & source.ack, + bus.ack.eq(1), + NextState("IDLE") + ) ) ) fsm.act("SEND_READ", - source.stb.eq(1), - source.eop.eq(1), - source.base_addr.eq(0), - source.count.eq(1), - source.be.eq(bus.sel), - source.we.eq(0), - source.data[2:].eq(bus.adr), - If(source.stb & source.ack, - NextState("WAIT_READ") + If(~self.ready, + NextState("SEND_ERROR") + ).Else( + source.stb.eq(1), + source.eop.eq(1), + source.base_addr.eq(0), + source.count.eq(1), + source.be.eq(bus.sel), + source.we.eq(0), + source.data[2:].eq(bus.adr), + If(source.stb & source.ack, + NextState("WAIT_READ") + ) ) ) fsm.act("WAIT_READ", sink.ack.eq(1), - If(sink.stb & sink.we, + If(~self.ready, + NextState("SEND_ERROR") + ).Elif(sink.stb & sink.we, bus.ack.eq(1), bus.dat_r.eq(sink.data), NextState("IDLE") From 9ba50098a83e6528c45654dc9264337a973facec Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 29 Aug 2017 17:31:01 +0200 Subject: [PATCH 22/45] gateware/test/serwb: use unittest for in test_etherbone --- artiq/gateware/test/serwb/test_etherbone.py | 28 ++++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/artiq/gateware/test/serwb/test_etherbone.py b/artiq/gateware/test/serwb/test_etherbone.py index 248e3f68e..963769a1f 100644 --- a/artiq/gateware/test/serwb/test_etherbone.py +++ b/artiq/gateware/test/serwb/test_etherbone.py @@ -1,3 +1,6 @@ +import unittest +import random + from migen import * from misoc.interconnect.wishbone import SRAM @@ -25,7 +28,7 @@ class DUT(Module): master_packetizer = packet.Packetizer() self.submodules += master_depacketizer, master_packetizer master_etherbone = etherbone.Etherbone(mode="master") - master_sram = SRAM(1024, bus=master_etherbone.wishbone.bus) + master_sram = SRAM(64, bus=master_etherbone.wishbone.bus) self.submodules += master_etherbone, master_sram self.comb += [ master_depacketizer.source.connect(master_etherbone.sink), @@ -53,14 +56,15 @@ class DUT(Module): self.wishbone = slave_etherbone.wishbone.bus -def main_generator(dut): - for i in range(8): - yield from dut.wishbone.write(0x100 + i, i) - for i in range(8): - data = (yield from dut.wishbone.read(0x100 + i)) - print("0x{:08x}".format(data)) - - -if __name__ == "__main__": - dut = DUT() - run_simulation(dut, main_generator(dut), vcd_name="sim.vcd") +class TestEtherbone(unittest.TestCase): + def test_write_read_sram(self): + dut = DUT() + prng = random.Random(1) + def generator(dut): + datas = [prng.randrange(0, 2**32-1) for i in range(16)] + for i in range(16): + yield from dut.wishbone.write(i, datas[i]) + for i in range(16): + data = (yield from dut.wishbone.read(i)) + self.assertEqual(data, datas[i]) + run_simulation(dut, generator(dut)) From 41d57d64f6acdaa8c6034b2fe397886be54199fa Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 30 Aug 2017 14:31:44 +0200 Subject: [PATCH 23/45] gateware/serwb: SERWBPLL, SERWBPHY, SERWBCore and add checks in delay finding to verify the sampling window --- artiq/gateware/serwb/__init__.py | 2 +- artiq/gateware/serwb/core.py | 37 +++++++++ artiq/gateware/serwb/etherbone.py | 60 +++++++------- artiq/gateware/serwb/kusphy.py | 57 ------------- artiq/gateware/serwb/phy.py | 130 ++++++++++++++++++++++++++---- artiq/gateware/serwb/s7phy.py | 57 ------------- 6 files changed, 182 insertions(+), 161 deletions(-) create mode 100644 artiq/gateware/serwb/core.py diff --git a/artiq/gateware/serwb/__init__.py b/artiq/gateware/serwb/__init__.py index 632008428..3ebf3f028 100644 --- a/artiq/gateware/serwb/__init__.py +++ b/artiq/gateware/serwb/__init__.py @@ -1 +1 @@ -from artiq.gateware.serwb import s7phy, kusphy, phy, packet, etherbone +from artiq.gateware.serwb import s7phy, kusphy, phy, core, packet, etherbone diff --git a/artiq/gateware/serwb/core.py b/artiq/gateware/serwb/core.py new file mode 100644 index 000000000..d96f77dc6 --- /dev/null +++ b/artiq/gateware/serwb/core.py @@ -0,0 +1,37 @@ +from migen import * + +from misoc.interconnect import stream + +from artiq.gateware.serwb.packet import Depacketizer, Packetizer +from artiq.gateware.serwb.etherbone import Etherbone + + +class SERWBCore(Module): + def __init__(self, phy, clk_freq, mode): + self.submodules.etherbone = etherbone = Etherbone(mode) + depacketizer = Depacketizer(clk_freq) + packetizer = Packetizer() + self.submodules += depacketizer, packetizer + tx_cdc = stream.AsyncFIFO([("data", 32)], 8) + tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serdes"})(tx_cdc) + self.submodules += tx_cdc + rx_cdc = stream.AsyncFIFO([("data", 32)], 8) + rx_cdc = ClockDomainsRenamer({"write": "serdes", "read": "sys"})(rx_cdc) + self.submodules += rx_cdc + self.comb += [ + # core <--> etherbone + depacketizer.source.connect(etherbone.sink), + etherbone.source.connect(packetizer.sink), + + # core --> serdes + packetizer.source.connect(tx_cdc.sink), + If(tx_cdc.source.stb & phy.init.ready, + phy.serdes.tx_data.eq(tx_cdc.source.data) + ), + tx_cdc.source.ack.eq(phy.init.ready), + + # serdes --> core + rx_cdc.sink.stb.eq(phy.init.ready), + rx_cdc.sink.data.eq(phy.serdes.rx_data), + rx_cdc.source.connect(depacketizer.sink), + ] diff --git a/artiq/gateware/serwb/etherbone.py b/artiq/gateware/serwb/etherbone.py index 9ac7bdb7e..231299bed 100644 --- a/artiq/gateware/serwb/etherbone.py +++ b/artiq/gateware/serwb/etherbone.py @@ -18,7 +18,7 @@ from misoc.interconnect import wishbone from artiq.gateware.serwb.packet import * -class Packetizer(Module): +class _Packetizer(Module): def __init__(self, sink_description, source_description, header): self.sink = sink = stream.Endpoint(sink_description) self.source = source = stream.Endpoint(source_description) @@ -108,7 +108,7 @@ class Packetizer(Module): ) -class Depacketizer(Module): +class _Depacketizer(Module): def __init__(self, sink_description, source_description, header): self.sink = sink = stream.Endpoint(sink_description) self.source = source = stream.Endpoint(source_description) @@ -275,22 +275,22 @@ def etherbone_mmap_description(dw): # etherbone packet -class EtherbonePacketPacketizer(Packetizer): +class _EtherbonePacketPacketizer(_Packetizer): def __init__(self): - Packetizer.__init__(self, + _Packetizer.__init__(self, etherbone_packet_description(32), user_description(32), etherbone_packet_header) -class EtherbonePacketTX(Module): +class _EtherbonePacketTX(Module): def __init__(self): self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32)) self.source = source = stream.Endpoint(user_description(32)) # # # - self.submodules.packetizer = packetizer = EtherbonePacketPacketizer() + self.submodules.packetizer = packetizer = _EtherbonePacketPacketizer() self.comb += [ packetizer.sink.stb.eq(sink.stb), packetizer.sink.eop.eq(sink.eop), @@ -321,22 +321,22 @@ class EtherbonePacketTX(Module): ) -class EtherbonePacketDepacketizer(Depacketizer): +class _EtherbonePacketDepacketizer(_Depacketizer): def __init__(self): - Depacketizer.__init__(self, + _Depacketizer.__init__(self, user_description(32), etherbone_packet_description(32), etherbone_packet_header) -class EtherbonePacketRX(Module): +class _EtherbonePacketRX(Module): def __init__(self): self.sink = sink = stream.Endpoint(user_description(32)) self.source = source = stream.Endpoint(etherbone_packet_user_description(32)) # # # - self.submodules.depacketizer = depacketizer = EtherbonePacketDepacketizer() + self.submodules.depacketizer = depacketizer = _EtherbonePacketDepacketizer() self.comb += sink.connect(depacketizer.sink) self.submodules.fsm = fsm = FSM(reset_state="IDLE") @@ -385,10 +385,10 @@ class EtherbonePacketRX(Module): ) -class EtherbonePacket(Module): +class _EtherbonePacket(Module): def __init__(self, port_sink, port_source): - self.submodules.tx = tx = EtherbonePacketTX() - self.submodules.rx = rx = EtherbonePacketRX() + self.submodules.tx = tx = _EtherbonePacketTX() + self.submodules.rx = rx = _EtherbonePacketRX() self.comb += [ tx.source.connect(port_sink), port_source.connect(rx.sink) @@ -397,23 +397,23 @@ class EtherbonePacket(Module): # etherbone record -class EtherboneRecordPacketizer(Packetizer): +class _EtherboneRecordPacketizer(_Packetizer): def __init__(self): - Packetizer.__init__(self, + _Packetizer.__init__(self, etherbone_record_description(32), etherbone_packet_user_description(32), etherbone_record_header) -class EtherboneRecordDepacketizer(Depacketizer): +class _EtherboneRecordDepacketizer(_Depacketizer): def __init__(self): - Depacketizer.__init__(self, + _Depacketizer.__init__(self, etherbone_packet_user_description(32), etherbone_record_description(32), etherbone_record_header) -class EtherboneRecordReceiver(Module): +class _EtherboneRecordReceiver(Module): def __init__(self, buffer_depth=256): self.sink = sink = stream.Endpoint(etherbone_record_description(32)) self.source = source = stream.Endpoint(etherbone_mmap_description(32)) @@ -496,7 +496,7 @@ class EtherboneRecordReceiver(Module): ) -class EtherboneRecordSender(Module): +class _EtherboneRecordSender(Module): def __init__(self, buffer_depth=256): self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) self.source = source = stream.Endpoint(etherbone_record_description(32)) @@ -545,7 +545,7 @@ class EtherboneRecordSender(Module): ) -class EtherboneRecord(Module): +class _EtherboneRecord(Module): def __init__(self): self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32)) self.source = source = stream.Endpoint(etherbone_packet_user_description(32)) @@ -553,16 +553,16 @@ class EtherboneRecord(Module): # # # # receive record, decode it and generate mmap stream - self.submodules.depacketizer = depacketizer = EtherboneRecordDepacketizer() - self.submodules.receiver = receiver = EtherboneRecordReceiver() + self.submodules.depacketizer = depacketizer = _EtherboneRecordDepacketizer() + self.submodules.receiver = receiver = _EtherboneRecordReceiver() self.comb += [ sink.connect(depacketizer.sink), depacketizer.source.connect(receiver.sink) ] # receive mmap stream, encode it and send records - self.submodules.sender = sender = EtherboneRecordSender() - self.submodules.packetizer = packetizer = EtherboneRecordPacketizer() + self.submodules.sender = sender = _EtherboneRecordSender() + self.submodules.packetizer = packetizer = _EtherboneRecordPacketizer() self.comb += [ sender.source.connect(packetizer.sink), packetizer.source.connect(source), @@ -574,7 +574,7 @@ class EtherboneRecord(Module): # etherbone wishbone -class EtherboneWishboneMaster(Module): +class _EtherboneWishboneMaster(Module): def __init__(self): self.sink = sink = stream.Endpoint(etherbone_mmap_description(32)) self.source = source = stream.Endpoint(etherbone_mmap_description(32)) @@ -642,7 +642,7 @@ class EtherboneWishboneMaster(Module): ) -class EtherboneWishboneSlave(Module): +class _EtherboneWishboneSlave(Module): def __init__(self): self.bus = bus = wishbone.Interface() self.ready = Signal(reset=1) @@ -723,12 +723,12 @@ class Etherbone(Module): # # # - self.submodules.packet = EtherbonePacket(source, sink) - self.submodules.record = EtherboneRecord() + self.submodules.packet = _EtherbonePacket(source, sink) + self.submodules.record = _EtherboneRecord() if mode == "master": - self.submodules.wishbone = EtherboneWishboneMaster() + self.submodules.wishbone = _EtherboneWishboneMaster() elif mode == "slave": - self.submodules.wishbone = EtherboneWishboneSlave() + self.submodules.wishbone = _EtherboneWishboneSlave() else: raise ValueError diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py index 412f469e4..49e423ff8 100644 --- a/artiq/gateware/serwb/kusphy.py +++ b/artiq/gateware/serwb/kusphy.py @@ -6,63 +6,6 @@ from migen.genlib.misc import BitSlip from misoc.cores.code_8b10b import Encoder, Decoder -class KUSSerdesPLL(Module): - def __init__(self, refclk_freq, linerate, vco_div=1): - assert refclk_freq == 125e6 - assert linerate == 1.25e9 - - self.lock = Signal() - self.refclk = Signal() - self.serdes_clk = Signal() - self.serdes_20x_clk = Signal() - self.serdes_5x_clk = Signal() - - # # # - - #---------------------- - # refclk: 125MHz - # vco: 1250MHz - #---------------------- - # serdes: 31.25MHz - # serdes_20x: 625MHz - # serdes_5x: 156.25MHz - #---------------------- - self.linerate = linerate - - pll_locked = Signal() - pll_fb = Signal() - pll_serdes_clk = Signal() - pll_serdes_20x_clk = Signal() - pll_serdes_5x_clk = Signal() - self.specials += [ - Instance("PLLE2_BASE", - p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, - - # VCO @ 1.25GHz / vco_div - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0, - p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div, - i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, - o_CLKFBOUT=pll_fb, - - # 31.25MHz: serdes - p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, - o_CLKOUT0=pll_serdes_clk, - - # 625MHz: serdes_20x - p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, - o_CLKOUT1=pll_serdes_20x_clk, - - # 156.25MHz: serdes_5x - p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, - o_CLKOUT2=pll_serdes_5x_clk - ), - Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), - Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), - Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) - ] - self.specials += MultiReg(pll_locked, self.lock) - - class KUSSerdes(Module): def __init__(self, pll, pads, mode="master"): self.tx_data = Signal(32) diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py index 179e141f9..11808a9b3 100644 --- a/artiq/gateware/serwb/phy.py +++ b/artiq/gateware/serwb/phy.py @@ -4,6 +4,9 @@ from migen.genlib.misc import WaitTimer from misoc.interconnect.csr import * +from artiq.gateware.serwb.kusphy import KUSSerdes +from artiq.gateware.serwb.s7phy import S7Serdes + # Master <--> Slave synchronization: # 1) Master sends idle pattern (zeroes) to reset Slave. @@ -13,11 +16,11 @@ from misoc.interconnect.csr import * # 5) Slave stops sending K25.5 commas. # 6) Link is ready. -class SerdesMasterInit(Module): +class _SerdesMasterInit(Module): def __init__(self, serdes, taps): self.reset = Signal() - self.error = Signal() self.ready = Signal() + self.error = Signal() # # # @@ -72,6 +75,7 @@ class SerdesMasterInit(Module): If(serdes.rx_comma, timer.wait.eq(1), If(timer.done, + timer.wait.eq(0), NextValue(delay_min, delay), NextValue(delay_min_found, 1) ) @@ -82,7 +86,7 @@ class SerdesMasterInit(Module): If(~serdes.rx_comma, NextValue(delay_max, delay), NextValue(delay_max_found, 1), - NextState("RESET_SAMPLING_WINDOW") + NextState("CHECK_SAMPLING_WINDOW") ).Else( NextState("INC_DELAY_BITSLIP") ) @@ -93,12 +97,10 @@ class SerdesMasterInit(Module): fsm.act("INC_DELAY_BITSLIP", NextState("WAIT_STABLE"), If(delay == (taps - 1), - If(delay_min_found, - NextState("ERROR") - ), If(bitslip == (40 - 1), - NextValue(bitslip, 0) - ).Else( + NextState("ERROR") + ).Else( + NextValue(delay_min_found, 0), NextValue(bitslip, bitslip + 1) ), NextValue(delay, 0), @@ -110,6 +112,17 @@ class SerdesMasterInit(Module): ), serdes.tx_comma.eq(1) ) + fsm.act("CHECK_SAMPLING_WINDOW", + If((delay_min == 0) | + (delay_max == (taps - 1)) | + ((delay_max - delay_min) < taps//16), + NextValue(delay_min_found, 0), + NextValue(delay_max_found, 0), + NextState("WAIT_STABLE") + ).Else( + NextState("RESET_SAMPLING_WINDOW") + ) + ) fsm.act("RESET_SAMPLING_WINDOW", NextValue(delay, 0), serdes.rx_delay_rst.eq(1), @@ -143,7 +156,7 @@ class SerdesMasterInit(Module): ) -class SerdesSlaveInit(Module, AutoCSR): +class _SerdesSlaveInit(Module, AutoCSR): def __init__(self, serdes, taps): self.reset = Signal() self.ready = Signal() @@ -199,7 +212,7 @@ class SerdesSlaveInit(Module, AutoCSR): If(~serdes.rx_comma, NextValue(delay_max, delay), NextValue(delay_max_found, 1), - NextState("RESET_SAMPLING_WINDOW") + NextState("CHECK_SAMPLING_WINDOW") ).Else( NextState("INC_DELAY_BITSLIP") ) @@ -210,12 +223,10 @@ class SerdesSlaveInit(Module, AutoCSR): fsm.act("INC_DELAY_BITSLIP", NextState("WAIT_STABLE"), If(delay == (taps - 1), - If(delay_min_found, - NextState("ERROR") - ), If(bitslip == (40 - 1), - NextValue(bitslip, 0) - ).Else( + NextState("ERROR") + ).Else( + NextValue(delay_min_found, 0), NextValue(bitslip, bitslip + 1) ), NextValue(delay, 0), @@ -227,6 +238,17 @@ class SerdesSlaveInit(Module, AutoCSR): ), serdes.tx_idle.eq(1) ) + fsm.act("CHECK_SAMPLING_WINDOW", + If((delay_min == 0) | + (delay_max == (taps - 1)) | + ((delay_max - delay_min) < taps//16), + NextValue(delay_min_found, 0), + NextValue(delay_max_found, 0), + NextState("WAIT_STABLE") + ).Else( + NextState("RESET_SAMPLING_WINDOW") + ) + ) fsm.act("RESET_SAMPLING_WINDOW", NextValue(delay, 0), serdes.rx_delay_rst.eq(1), @@ -266,7 +288,7 @@ class SerdesSlaveInit(Module, AutoCSR): ) -class SerdesControl(Module, AutoCSR): +class _SerdesControl(Module, AutoCSR): def __init__(self, init, mode="master"): if mode == "master": self.reset = CSR() @@ -294,3 +316,79 @@ class SerdesControl(Module, AutoCSR): self.delay_max.status.eq(init.delay_max), self.bitslip.status.eq(init.bitslip) ] + + +class SERWBPLL(Module): + def __init__(self, refclk_freq, linerate, vco_div=1): + assert refclk_freq == 125e6 + assert linerate == 1.25e9 + + self.lock = Signal() + self.refclk = Signal() + self.serdes_clk = Signal() + self.serdes_20x_clk = Signal() + self.serdes_5x_clk = Signal() + + # # # + + #---------------------- + # refclk: 125MHz + # vco: 1250MHz + #---------------------- + # serdes: 31.25MHz + # serdes_20x: 625MHz + # serdes_5x: 156.25MHz + #---------------------- + self.linerate = linerate + + pll_locked = Signal() + pll_fb = Signal() + pll_serdes_clk = Signal() + pll_serdes_20x_clk = Signal() + pll_serdes_5x_clk = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1.25GHz / vco_div + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0, + p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div, + i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, + o_CLKFBOUT=pll_fb, + + # 31.25MHz: serdes + p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, + o_CLKOUT0=pll_serdes_clk, + + # 625MHz: serdes_20x + p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, + o_CLKOUT1=pll_serdes_20x_clk, + + # 156.25MHz: serdes_5x + p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, + o_CLKOUT2=pll_serdes_5x_clk + ), + Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), + Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), + Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) + ] + self.specials += MultiReg(pll_locked, self.lock) + + + +class SERWBPHY(Module, AutoCSR): + def __init__(self, device, pll, pads, mode="master"): + assert mode in ["master", "slave"] + if device[:4] == "xcku": + taps = 512 + self.submodules.serdes = KUSSerdes(pll, pads, mode) + elif device[:4] == "xc7a": + taps = 32 + self.submodules.serdes = S7Serdes(pll, pads, mode) + else: + raise NotImplementedError + if mode == "master": + self.submodules.init = _SerdesMasterInit(self.serdes, taps) + else: + self.submodules.init = _SerdesSlaveInit(self.serdes, taps) + self.submodules.control = _SerdesControl(self.init, mode) diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py index 2b5a0473c..fd4e2657a 100644 --- a/artiq/gateware/serwb/s7phy.py +++ b/artiq/gateware/serwb/s7phy.py @@ -6,63 +6,6 @@ from migen.genlib.misc import BitSlip from misoc.cores.code_8b10b import Encoder, Decoder -class S7SerdesPLL(Module): - def __init__(self, refclk_freq, linerate, vco_div=1): - assert refclk_freq == 125e6 - assert linerate == 1.25e9 - - self.lock = Signal() - self.refclk = Signal() - self.serdes_clk = Signal() - self.serdes_20x_clk = Signal() - self.serdes_5x_clk = Signal() - - # # # - - #---------------------- - # refclk: 125MHz - # vco: 1250MHz - #---------------------- - # serdes: 31.25MHz - # serdes_20x: 625MHz - # serdes_5x: 156.25MHz - #---------------------- - self.linerate = linerate - - pll_locked = Signal() - pll_fb = Signal() - pll_serdes_clk = Signal() - pll_serdes_20x_clk = Signal() - pll_serdes_5x_clk = Signal() - self.specials += [ - Instance("PLLE2_BASE", - p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, - - # VCO @ 1.25GHz / vco_div - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0, - p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div, - i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, - o_CLKFBOUT=pll_fb, - - # 31.25MHz: serdes - p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, - o_CLKOUT0=pll_serdes_clk, - - # 625MHz: serdes_20x - p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, - o_CLKOUT1=pll_serdes_20x_clk, - - # 156.25MHz: serdes_5x - p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, - o_CLKOUT2=pll_serdes_5x_clk - ), - Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), - Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), - Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) - ] - self.specials += MultiReg(pll_locked, self.lock) - - class S7Serdes(Module): def __init__(self, pll, pads, mode="master"): self.tx_data = Signal(32) From 32ca51faee1764a97aa5183ac5635ddb477d2e9f Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 30 Aug 2017 14:40:46 +0200 Subject: [PATCH 24/45] gateware/targets/sayma_amc_standalone/rtm: use new serwb modules --- .../gateware/targets/sayma_amc_standalone.py | 63 ++++++------------- artiq/gateware/targets/sayma_rtm.py | 62 +++++------------- 2 files changed, 34 insertions(+), 91 deletions(-) diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index 554964b74..9c3a94cd7 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -58,59 +58,32 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): # AMC/RTM serwb # TODO: cleanup (same comments as in sayma_rtm.py) - serwb_pll = serwb.kusphy.KUSSerdesPLL(self.clk_freq, 1.25e9, vco_div=2) + + # serwb SERDES + serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=1) self.comb += serwb_pll.refclk.eq(self.crg.cd_sys.clk) self.submodules += serwb_pll serwb_pads = platform.request("amc_rtm_serwb") - serwb_serdes = serwb.kusphy.KUSSerdes(serwb_pll, serwb_pads, mode="master") - self.submodules += serwb_serdes - serwb_init = serwb.phy.SerdesMasterInit(serwb_serdes, taps=512) - self.submodules += serwb_init - self.submodules.serwb_control = serwb.phy.SerdesControl(serwb_init, mode="master") - self.csr_devices.append("serwb_control") + serwb_phy = serwb.phy.SERWBPHY(platform.device, serwb_pll, serwb_pads, mode="slave") + self.submodules.serwb_phy = serwb_phy + self.csr_devices.append("serwb_phy") - serwb_serdes.cd_serdes.clk.attr.add("keep") - serwb_serdes.cd_serdes_20x.clk.attr.add("keep") - serwb_serdes.cd_serdes_5x.clk.attr.add("keep") - platform.add_period_constraint(serwb_serdes.cd_serdes.clk, 32.0), - platform.add_period_constraint(serwb_serdes.cd_serdes_20x.clk, 1.6), - platform.add_period_constraint(serwb_serdes.cd_serdes_5x.clk, 6.4) + serwb_phy.serdes.cd_serdes.clk.attr.add("keep") + serwb_phy.serdes.cd_serdes_20x.clk.attr.add("keep") + serwb_phy.serdes.cd_serdes_5x.clk.attr.add("keep") + platform.add_period_constraint(serwb_phy.serdes.cd_serdes.clk, 32.0), + platform.add_period_constraint(serwb_phy.serdes.cd_serdes_20x.clk, 1.6), + platform.add_period_constraint(serwb_phy.serdes.cd_serdes_5x.clk, 6.4) platform.add_false_path_constraints( self.crg.cd_sys.clk, - serwb_serdes.cd_serdes.clk, - serwb_serdes.cd_serdes_5x.clk) + serwb_phy.serdes.cd_serdes.clk, + serwb_phy.serdes.cd_serdes_5x.clk) - serwb_depacketizer = serwb.packet.Depacketizer(self.clk_freq) - serwb_packetizer = serwb.packet.Packetizer() - self.submodules += serwb_depacketizer, serwb_packetizer - serwb_etherbone = serwb.etherbone.Etherbone(mode="slave") - self.submodules += serwb_etherbone - self.comb += serwb_etherbone.wishbone.ready.eq(serwb_init.ready) - serwb_tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serdes"})( - stream.AsyncFIFO([("data", 32)], 8)) - self.submodules += serwb_tx_cdc - serwb_rx_cdc = ClockDomainsRenamer({"write": "serdes", "read": "sys"})( - stream.AsyncFIFO([("data", 32)], 8)) - self.submodules += serwb_rx_cdc - self.comb += [ - # core <--> etherbone - serwb_depacketizer.source.connect(serwb_etherbone.sink), - serwb_etherbone.source.connect(serwb_packetizer.sink), - - # core --> serdes - serwb_packetizer.source.connect(serwb_tx_cdc.sink), - If(serwb_tx_cdc.source.stb & serwb_init.ready, - serwb_serdes.tx_data.eq(serwb_tx_cdc.source.data) - ), - serwb_tx_cdc.source.ack.eq(serwb_init.ready), - - # serdes --> core - serwb_rx_cdc.sink.stb.eq(serwb_init.ready), - serwb_rx_cdc.sink.data.eq(serwb_serdes.rx_data), - serwb_rx_cdc.source.connect(serwb_depacketizer.sink), - ] - self.add_wb_slave(self.mem_map["serwb"], 8192, serwb_etherbone.wishbone.bus) + # serwb slave + serwb_core = serwb.core.SERWBCore(serwb_phy, int(self.clk_freq), mode="slave") + self.submodules += serwb_core + self.add_wb_slave(self.mem_map["serwb"], 8192, serwb_core.etherbone.wishbone.bus) # RTIO rtio_channels = [] diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index ef378b484..08afb202d 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -98,62 +98,32 @@ class SaymaRTM(Module): csr_devices.append("converter_spi") self.comb += platform.request("hmc7043_reset").eq(0) - # TODO: push all those serwb bits into library modules - # maybe keep only 3 user-visible modules: serwb PLL, serwb PHY, and serwb core - # TODO: after this is done, stop exposing internal modules in serwb/__init__.py # TODO: avoid having a "serdes" clock domain at the top level, rename to "serwb_serdes" or similar. # TODO: the above also applies to sayma_amc_standalone.py. # serwb SERDES - serwb_pll = serwb.s7phy.S7SerdesPLL(125e6, 1.25e9, vco_div=1) + serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=1) self.submodules += serwb_pll - serwb_serdes = serwb.s7phy.S7Serdes(serwb_pll, platform.request("amc_rtm_serwb"), mode="slave") - self.submodules += serwb_serdes - serwb_init = serwb.phy.SerdesSlaveInit(serwb_serdes, taps=32) - self.submodules += serwb_init - self.comb += self.crg.reset.eq(serwb_init.reset) + serwb_pads = platform.request("amc_rtm_serwb") + serwb_phy = serwb.phy.SERWBPHY(platform.device, serwb_pll, serwb_pads, mode="slave") + self.submodules.serwb_phy = serwb_phy + self.comb += self.crg.reset.eq(serwb_phy.init.reset) - serwb_serdes.cd_serdes.clk.attr.add("keep") - serwb_serdes.cd_serdes_20x.clk.attr.add("keep") - serwb_serdes.cd_serdes_5x.clk.attr.add("keep") - platform.add_period_constraint(serwb_serdes.cd_serdes.clk, 32.0), - platform.add_period_constraint(serwb_serdes.cd_serdes_20x.clk, 1.6), - platform.add_period_constraint(serwb_serdes.cd_serdes_5x.clk, 6.4) + serwb_phy.serdes.cd_serdes.clk.attr.add("keep") + serwb_phy.serdes.cd_serdes_20x.clk.attr.add("keep") + serwb_phy.serdes.cd_serdes_5x.clk.attr.add("keep") + platform.add_period_constraint(serwb_phy.serdes.cd_serdes.clk, 32.0), + platform.add_period_constraint(serwb_phy.serdes.cd_serdes_20x.clk, 1.6), + platform.add_period_constraint(serwb_phy.serdes.cd_serdes_5x.clk, 6.4) platform.add_false_path_constraints( self.crg.cd_sys.clk, - serwb_serdes.cd_serdes.clk, - serwb_serdes.cd_serdes_5x.clk) + serwb_phy.serdes.cd_serdes.clk, + serwb_phy.serdes.cd_serdes_5x.clk) # serwb master - serwb_depacketizer = serwb.packet.Depacketizer(int(clk_freq)) - serwb_packetizer = serwb.packet.Packetizer() - self.submodules += serwb_depacketizer, serwb_packetizer - serwb_etherbone = serwb.etherbone.Etherbone(mode="master") - self.submodules += serwb_etherbone - serwb_tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serdes"})( - stream.AsyncFIFO([("data", 32)], 8)) - self.submodules += serwb_tx_cdc - serwb_rx_cdc = ClockDomainsRenamer({"write": "serdes", "read": "sys"})( - stream.AsyncFIFO([("data", 32)], 8)) - self.submodules += serwb_rx_cdc - self.comb += [ - # core <--> etherbone - serwb_depacketizer.source.connect(serwb_etherbone.sink), - serwb_etherbone.source.connect(serwb_packetizer.sink), - - # core --> serdes - serwb_packetizer.source.connect(serwb_tx_cdc.sink), - If(serwb_tx_cdc.source.stb & serwb_init.ready, - serwb_serdes.tx_data.eq(serwb_tx_cdc.source.data) - ), - serwb_tx_cdc.source.ack.eq(serwb_init.ready), - - # serdes --> core - serwb_rx_cdc.sink.stb.eq(serwb_init.ready), - serwb_rx_cdc.sink.data.eq(serwb_serdes.rx_data), - serwb_rx_cdc.source.connect(serwb_depacketizer.sink), - ] + serwb_core = serwb.core.SERWBCore(serwb_phy, int(clk_freq), mode="master") + self.submodules += serwb_core # process CSR devices and connect them to serwb self.csr_regions = [] @@ -169,7 +139,7 @@ class SaymaRTM(Module): wb_slaves.add(origin, CSR_RANGE_SIZE, bank.bus) self.csr_regions.append((name, origin, 32, csrs)) - self.submodules += wishbone.Decoder(serwb_etherbone.wishbone.bus, + self.submodules += wishbone.Decoder(serwb_core.etherbone.wishbone.bus, wb_slaves.get_interconnect_slaves(), register=True) From 96502330077d8ac62d040a2cca4d660185a9ab8e Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 30 Aug 2017 15:44:44 +0200 Subject: [PATCH 25/45] gateware/serwb: change serdes clock domain to serwb_serdes --- artiq/gateware/serwb/core.py | 4 +- artiq/gateware/serwb/kusphy.py | 60 +++++++++---------- artiq/gateware/serwb/phy.py | 46 +++++++------- artiq/gateware/serwb/s7phy.py | 50 ++++++++-------- .../gateware/targets/sayma_amc_standalone.py | 16 ++--- artiq/gateware/targets/sayma_rtm.py | 16 ++--- 6 files changed, 96 insertions(+), 96 deletions(-) diff --git a/artiq/gateware/serwb/core.py b/artiq/gateware/serwb/core.py index d96f77dc6..4435c3e40 100644 --- a/artiq/gateware/serwb/core.py +++ b/artiq/gateware/serwb/core.py @@ -13,10 +13,10 @@ class SERWBCore(Module): packetizer = Packetizer() self.submodules += depacketizer, packetizer tx_cdc = stream.AsyncFIFO([("data", 32)], 8) - tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serdes"})(tx_cdc) + tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serwb_serdes"})(tx_cdc) self.submodules += tx_cdc rx_cdc = stream.AsyncFIFO([("data", 32)], 8) - rx_cdc = ClockDomainsRenamer({"write": "serdes", "read": "sys"})(rx_cdc) + rx_cdc = ClockDomainsRenamer({"write": "serwb_serdes", "read": "sys"})(rx_cdc) self.submodules += rx_cdc self.comb += [ # core <--> etherbone diff --git a/artiq/gateware/serwb/kusphy.py b/artiq/gateware/serwb/kusphy.py index 49e423ff8..74cb16fbf 100644 --- a/artiq/gateware/serwb/kusphy.py +++ b/artiq/gateware/serwb/kusphy.py @@ -24,9 +24,9 @@ class KUSSerdes(Module): # # # - self.submodules.encoder = ClockDomainsRenamer("serdes")( + self.submodules.encoder = ClockDomainsRenamer("serwb_serdes")( Encoder(4, True)) - self.decoders = [ClockDomainsRenamer("serdes")( + self.decoders = [ClockDomainsRenamer("serwb_serdes")( Decoder(True)) for _ in range(4)] self.submodules += self.decoders @@ -37,16 +37,16 @@ class KUSSerdes(Module): # - linerate/10 slave refclk generated on clk_pads # In Slave mode: # - linerate/10 pll refclk provided by clk_pads - self.clock_domains.cd_serdes = ClockDomain() - self.clock_domains.cd_serdes_5x = ClockDomain() - self.clock_domains.cd_serdes_20x = ClockDomain(reset_less=True) + self.clock_domains.cd_serwb_serdes = ClockDomain() + self.clock_domains.cd_serwb_serdes_5x = ClockDomain() + self.clock_domains.cd_serwb_serdes_20x = ClockDomain(reset_less=True) self.comb += [ - self.cd_serdes.clk.eq(pll.serdes_clk), - self.cd_serdes_5x.clk.eq(pll.serdes_5x_clk), - self.cd_serdes_20x.clk.eq(pll.serdes_20x_clk) + self.cd_serwb_serdes.clk.eq(pll.serwb_serdes_clk), + self.cd_serwb_serdes_5x.clk.eq(pll.serwb_serdes_5x_clk), + self.cd_serwb_serdes_20x.clk.eq(pll.serwb_serdes_20x_clk) ] - self.specials += AsyncResetSynchronizer(self.cd_serdes, ~pll.lock) - self.comb += self.cd_serdes_5x.rst.eq(self.cd_serdes.rst) + self.specials += AsyncResetSynchronizer(self.cd_serwb_serdes, ~pll.lock) + self.comb += self.cd_serwb_serdes_5x.rst.eq(self.cd_serwb_serdes.rst) # control/status cdc tx_idle = Signal() @@ -59,20 +59,20 @@ class KUSSerdes(Module): rx_delay_en_vtc = Signal() rx_delay_ce = Signal() self.specials += [ - MultiReg(self.tx_idle, tx_idle, "serdes"), - MultiReg(self.tx_comma, tx_comma, "serdes"), + MultiReg(self.tx_idle, tx_idle, "serwb_serdes"), + MultiReg(self.tx_comma, tx_comma, "serwb_serdes"), MultiReg(rx_idle, self.rx_idle, "sys"), MultiReg(rx_comma, self.rx_comma, "sys"), - MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serdes"), - MultiReg(self.rx_delay_inc, rx_delay_inc, "serdes_5x"), - MultiReg(self.rx_delay_en_vtc, rx_delay_en_vtc, "serdes_5x") + MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serwb_serdes"), + MultiReg(self.rx_delay_inc, rx_delay_inc, "serwb_serdes_5x"), + MultiReg(self.rx_delay_en_vtc, rx_delay_en_vtc, "serwb_serdes_5x") ] - self.submodules.do_rx_delay_rst = PulseSynchronizer("sys", "serdes_5x") + self.submodules.do_rx_delay_rst = PulseSynchronizer("sys", "serwb_serdes_5x") self.comb += [ rx_delay_rst.eq(self.do_rx_delay_rst.o), self.do_rx_delay_rst.i.eq(self.rx_delay_rst) ] - self.submodules.do_rx_delay_ce = PulseSynchronizer("sys", "serdes_5x") + self.submodules.do_rx_delay_ce = PulseSynchronizer("sys", "serwb_serdes_5x") self.comb += [ rx_delay_ce.eq(self.do_rx_delay_ce.o), self.do_rx_delay_ce.i.eq(self.rx_delay_ce) @@ -80,7 +80,7 @@ class KUSSerdes(Module): # tx clock (linerate/10) if mode == "master": - self.submodules.tx_clk_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.submodules.tx_clk_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x") self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) | (0b1111100000 << 20) | (0b1111100000 << 10) | @@ -92,8 +92,8 @@ class KUSSerdes(Module): p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, o_OQ=clk_o, - i_RST=ResetSignal("serdes"), - i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_RST=ResetSignal("serwb_serdes"), + i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"), i_D=self.tx_clk_gearbox.o ), Instance("OBUFDS", @@ -105,7 +105,7 @@ class KUSSerdes(Module): # tx datapath # tx_data -> encoders -> gearbox -> serdes - self.submodules.tx_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.submodules.tx_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x") self.comb += [ If(tx_comma, self.encoder.k[0].eq(1), @@ -117,7 +117,7 @@ class KUSSerdes(Module): self.encoder.d[3].eq(self.tx_data[24:32]) ) ] - self.sync.serdes += \ + self.sync.serwb_serdes += \ If(tx_idle, self.tx_gearbox.i.eq(0) ).Else( @@ -131,8 +131,8 @@ class KUSSerdes(Module): p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, o_OQ=serdes_o, - i_RST=ResetSignal("serdes"), - i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_RST=ResetSignal("serwb_serdes"), + i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"), i_D=self.tx_gearbox.o ), Instance("OBUFDS", @@ -166,8 +166,8 @@ class KUSSerdes(Module): # rx datapath # serdes -> gearbox -> bitslip -> decoders -> rx_data - self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") - self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) + self.submodules.rx_gearbox = Gearbox(8, "serwb_serdes_5x", 40, "serwb_serdes") + self.submodules.rx_bitslip = ClockDomainsRenamer("serwb_serdes")(BitSlip(40)) serdes_i_nodelay = Signal() self.specials += [ @@ -187,7 +187,7 @@ class KUSSerdes(Module): p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN", p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0, - i_CLK=ClockSignal("serdes_5x"), + i_CLK=ClockSignal("serwb_serdes_5x"), i_RST=rx_delay_rst, i_LOAD=0, i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, i_CE=rx_delay_ce, @@ -198,10 +198,10 @@ class KUSSerdes(Module): p_DATA_WIDTH=8, i_D=serdes_i_delayed, - i_RST=ResetSignal("serdes"), + i_RST=ResetSignal("serwb_serdes"), i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, - i_CLK=ClockSignal("serdes_20x"), i_CLK_B=~ClockSignal("serdes_20x"), - i_CLKDIV=ClockSignal("serdes_5x"), + i_CLK=ClockSignal("serwb_serdes_20x"), i_CLK_B=~ClockSignal("serwb_serdes_20x"), + i_CLKDIV=ClockSignal("serwb_serdes_5x"), o_Q=serdes_q ) ] diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py index 11808a9b3..09b17763f 100644 --- a/artiq/gateware/serwb/phy.py +++ b/artiq/gateware/serwb/phy.py @@ -325,27 +325,27 @@ class SERWBPLL(Module): self.lock = Signal() self.refclk = Signal() - self.serdes_clk = Signal() - self.serdes_20x_clk = Signal() - self.serdes_5x_clk = Signal() + self.serwb_serdes_clk = Signal() + self.serwb_serdes_20x_clk = Signal() + self.serwb_serdes_5x_clk = Signal() # # # - #---------------------- - # refclk: 125MHz - # vco: 1250MHz - #---------------------- - # serdes: 31.25MHz - # serdes_20x: 625MHz - # serdes_5x: 156.25MHz - #---------------------- + #---------------------------- + # refclk: 125MHz + # vco: 1250MHz + #---------------------------- + # serwb_serdes: 31.25MHz + # serwb_serdes_20x: 625MHz + # serwb_serdes_5x: 156.25MHz + #---------------------------- self.linerate = linerate pll_locked = Signal() pll_fb = Signal() - pll_serdes_clk = Signal() - pll_serdes_20x_clk = Signal() - pll_serdes_5x_clk = Signal() + pll_serwb_serdes_clk = Signal() + pll_serwb_serdes_20x_clk = Signal() + pll_serwb_serdes_5x_clk = Signal() self.specials += [ Instance("PLLE2_BASE", p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, @@ -356,21 +356,21 @@ class SERWBPLL(Module): i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, - # 31.25MHz: serdes + # 31.25MHz: serwb_serdes p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0, - o_CLKOUT0=pll_serdes_clk, + o_CLKOUT0=pll_serwb_serdes_clk, - # 625MHz: serdes_20x + # 625MHz: serwb_serdes_20x p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0, - o_CLKOUT1=pll_serdes_20x_clk, + o_CLKOUT1=pll_serwb_serdes_20x_clk, - # 156.25MHz: serdes_5x + # 156.25MHz: serwb_serdes_5x p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0, - o_CLKOUT2=pll_serdes_5x_clk + o_CLKOUT2=pll_serwb_serdes_5x_clk ), - Instance("BUFG", i_I=pll_serdes_clk, o_O=self.serdes_clk), - Instance("BUFG", i_I=pll_serdes_20x_clk, o_O=self.serdes_20x_clk), - Instance("BUFG", i_I=pll_serdes_5x_clk, o_O=self.serdes_5x_clk) + Instance("BUFG", i_I=pll_serwb_serdes_clk, o_O=self.serwb_serdes_clk), + Instance("BUFG", i_I=pll_serwb_serdes_20x_clk, o_O=self.serwb_serdes_20x_clk), + Instance("BUFG", i_I=pll_serwb_serdes_5x_clk, o_O=self.serwb_serdes_5x_clk) ] self.specials += MultiReg(pll_locked, self.lock) diff --git a/artiq/gateware/serwb/s7phy.py b/artiq/gateware/serwb/s7phy.py index fd4e2657a..d64f5bbb5 100644 --- a/artiq/gateware/serwb/s7phy.py +++ b/artiq/gateware/serwb/s7phy.py @@ -23,9 +23,9 @@ class S7Serdes(Module): # # # - self.submodules.encoder = ClockDomainsRenamer("serdes")( + self.submodules.encoder = ClockDomainsRenamer("serwb_serdes")( Encoder(4, True)) - self.decoders = [ClockDomainsRenamer("serdes")( + self.decoders = [ClockDomainsRenamer("serwb_serdes")( Decoder(True)) for _ in range(4)] self.submodules += self.decoders @@ -36,16 +36,16 @@ class S7Serdes(Module): # - linerate/10 slave refclk generated on clk_pads # In Slave mode: # - linerate/10 pll refclk provided by clk_pads - self.clock_domains.cd_serdes = ClockDomain() - self.clock_domains.cd_serdes_5x = ClockDomain() - self.clock_domains.cd_serdes_20x = ClockDomain(reset_less=True) + self.clock_domains.cd_serwb_serdes = ClockDomain() + self.clock_domains.cd_serwb_serdes_5x = ClockDomain() + self.clock_domains.cd_serwb_serdes_20x = ClockDomain(reset_less=True) self.comb += [ - self.cd_serdes.clk.eq(pll.serdes_clk), - self.cd_serdes_5x.clk.eq(pll.serdes_5x_clk), - self.cd_serdes_20x.clk.eq(pll.serdes_20x_clk) + self.cd_serwb_serdes.clk.eq(pll.serwb_serdes_clk), + self.cd_serwb_serdes_5x.clk.eq(pll.serwb_serdes_5x_clk), + self.cd_serwb_serdes_20x.clk.eq(pll.serwb_serdes_20x_clk) ] - self.specials += AsyncResetSynchronizer(self.cd_serdes, ~pll.lock) - self.comb += self.cd_serdes_5x.rst.eq(self.cd_serdes.rst) + self.specials += AsyncResetSynchronizer(self.cd_serwb_serdes, ~pll.lock) + self.comb += self.cd_serwb_serdes_5x.rst.eq(self.cd_serwb_serdes.rst) # control/status cdc tx_idle = Signal() @@ -54,16 +54,16 @@ class S7Serdes(Module): rx_comma = Signal() rx_bitslip_value = Signal(6) self.specials += [ - MultiReg(self.tx_idle, tx_idle, "serdes"), - MultiReg(self.tx_comma, tx_comma, "serdes"), + MultiReg(self.tx_idle, tx_idle, "serwb_serdes"), + MultiReg(self.tx_comma, tx_comma, "serwb_serdes"), MultiReg(rx_idle, self.rx_idle, "sys"), MultiReg(rx_comma, self.rx_comma, "sys") ] - self.specials += MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serdes"), + self.specials += MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serwb_serdes"), # tx clock (linerate/10) if mode == "master": - self.submodules.tx_clk_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.submodules.tx_clk_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x") self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) | (0b1111100000 << 20) | (0b1111100000 << 10) | @@ -77,8 +77,8 @@ class S7Serdes(Module): o_OQ=clk_o, i_OCE=1, - i_RST=ResetSignal("serdes"), - i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_RST=ResetSignal("serwb_serdes"), + i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"), i_D1=self.tx_clk_gearbox.o[0], i_D2=self.tx_clk_gearbox.o[1], i_D3=self.tx_clk_gearbox.o[2], i_D4=self.tx_clk_gearbox.o[3], i_D5=self.tx_clk_gearbox.o[4], i_D6=self.tx_clk_gearbox.o[5], @@ -93,7 +93,7 @@ class S7Serdes(Module): # tx datapath # tx_data -> encoders -> gearbox -> serdes - self.submodules.tx_gearbox = Gearbox(40, "serdes", 8, "serdes_5x") + self.submodules.tx_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x") self.comb += [ If(tx_comma, self.encoder.k[0].eq(1), @@ -105,7 +105,7 @@ class S7Serdes(Module): self.encoder.d[3].eq(self.tx_data[24:32]) ) ] - self.sync.serdes += \ + self.sync.serwb_serdes += \ If(tx_idle, self.tx_gearbox.i.eq(0) ).Else( @@ -121,8 +121,8 @@ class S7Serdes(Module): o_OQ=serdes_o, i_OCE=1, - i_RST=ResetSignal("serdes"), - i_CLK=ClockSignal("serdes_20x"), i_CLKDIV=ClockSignal("serdes_5x"), + i_RST=ResetSignal("serwb_serdes"), + i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"), i_D1=self.tx_gearbox.o[0], i_D2=self.tx_gearbox.o[1], i_D3=self.tx_gearbox.o[2], i_D4=self.tx_gearbox.o[3], i_D5=self.tx_gearbox.o[4], i_D6=self.tx_gearbox.o[5], @@ -159,8 +159,8 @@ class S7Serdes(Module): # rx datapath # serdes -> gearbox -> bitslip -> decoders -> rx_data - self.submodules.rx_gearbox = Gearbox(8, "serdes_5x", 40, "serdes") - self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(40)) + self.submodules.rx_gearbox = Gearbox(8, "serwb_serdes_5x", 40, "serwb_serdes") + self.submodules.rx_bitslip = ClockDomainsRenamer("serwb_serdes")(BitSlip(40)) serdes_i_nodelay = Signal() self.specials += [ @@ -194,9 +194,9 @@ class S7Serdes(Module): i_DDLY=serdes_i_delayed, i_CE1=1, - i_RST=ResetSignal("serdes"), - i_CLK=ClockSignal("serdes_20x"), i_CLKB=~ClockSignal("serdes_20x"), - i_CLKDIV=ClockSignal("serdes_5x"), + i_RST=ResetSignal("serwb_serdes"), + i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKB=~ClockSignal("serwb_serdes_20x"), + i_CLKDIV=ClockSignal("serwb_serdes_5x"), i_BITSLIP=0, o_Q8=serdes_q[0], o_Q7=serdes_q[1], o_Q6=serdes_q[2], o_Q5=serdes_q[3], diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index 9c3a94cd7..8ef5cf1b8 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -69,16 +69,16 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): self.submodules.serwb_phy = serwb_phy self.csr_devices.append("serwb_phy") - serwb_phy.serdes.cd_serdes.clk.attr.add("keep") - serwb_phy.serdes.cd_serdes_20x.clk.attr.add("keep") - serwb_phy.serdes.cd_serdes_5x.clk.attr.add("keep") - platform.add_period_constraint(serwb_phy.serdes.cd_serdes.clk, 32.0), - platform.add_period_constraint(serwb_phy.serdes.cd_serdes_20x.clk, 1.6), - platform.add_period_constraint(serwb_phy.serdes.cd_serdes_5x.clk, 6.4) + serwb_phy.serdes.cd_serwb_serdes.clk.attr.add("keep") + serwb_phy.serdes.cd_serwb_serdes_20x.clk.attr.add("keep") + serwb_phy.serdes.cd_serwb_serdes_5x.clk.attr.add("keep") + platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes.clk, 32.0), + platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_20x.clk, 1.6), + platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_5x.clk, 6.4) platform.add_false_path_constraints( self.crg.cd_sys.clk, - serwb_phy.serdes.cd_serdes.clk, - serwb_phy.serdes.cd_serdes_5x.clk) + serwb_phy.serdes.cd_serwb_serdes.clk, + serwb_phy.serdes.cd_serwb_serdes_5x.clk) # serwb slave serwb_core = serwb.core.SERWBCore(serwb_phy, int(self.clk_freq), mode="slave") diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 08afb202d..797228e4e 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -110,16 +110,16 @@ class SaymaRTM(Module): self.submodules.serwb_phy = serwb_phy self.comb += self.crg.reset.eq(serwb_phy.init.reset) - serwb_phy.serdes.cd_serdes.clk.attr.add("keep") - serwb_phy.serdes.cd_serdes_20x.clk.attr.add("keep") - serwb_phy.serdes.cd_serdes_5x.clk.attr.add("keep") - platform.add_period_constraint(serwb_phy.serdes.cd_serdes.clk, 32.0), - platform.add_period_constraint(serwb_phy.serdes.cd_serdes_20x.clk, 1.6), - platform.add_period_constraint(serwb_phy.serdes.cd_serdes_5x.clk, 6.4) + serwb_phy.serdes.cd_serwb_serdes.clk.attr.add("keep") + serwb_phy.serdes.cd_serwb_serdes_20x.clk.attr.add("keep") + serwb_phy.serdes.cd_serwb_serdes_5x.clk.attr.add("keep") + platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes.clk, 32.0), + platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_20x.clk, 1.6), + platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_5x.clk, 6.4) platform.add_false_path_constraints( self.crg.cd_sys.clk, - serwb_phy.serdes.cd_serdes.clk, - serwb_phy.serdes.cd_serdes_5x.clk) + serwb_phy.serdes.cd_serwb_serdes.clk, + serwb_phy.serdes.cd_serwb_serdes_5x.clk) # serwb master serwb_core = serwb.core.SERWBCore(serwb_phy, int(clk_freq), mode="master") From 660f9856ec1fa4e1ab16b32988acd4c9720ced75 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 30 Aug 2017 17:59:10 +0200 Subject: [PATCH 26/45] gateware/serwb: add test for phy initialization --- artiq/gateware/serwb/phy.py | 8 +- .../test/serwb/test_serwb_phy_init.py | 164 ++++++++++++++++++ 2 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 artiq/gateware/test/serwb/test_serwb_phy_init.py diff --git a/artiq/gateware/serwb/phy.py b/artiq/gateware/serwb/phy.py index 09b17763f..49beb9240 100644 --- a/artiq/gateware/serwb/phy.py +++ b/artiq/gateware/serwb/phy.py @@ -17,7 +17,7 @@ from artiq.gateware.serwb.s7phy import S7Serdes # 6) Link is ready. class _SerdesMasterInit(Module): - def __init__(self, serdes, taps): + def __init__(self, serdes, taps, timeout=1024): self.reset = Signal() self.ready = Signal() self.error = Signal() @@ -31,7 +31,7 @@ class _SerdesMasterInit(Module): self.delay_max_found = delay_max_found = Signal() self.bitslip = bitslip = Signal(max=40) - timer = WaitTimer(1024) + timer = WaitTimer(timeout) self.submodules += timer self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) @@ -157,7 +157,7 @@ class _SerdesMasterInit(Module): class _SerdesSlaveInit(Module, AutoCSR): - def __init__(self, serdes, taps): + def __init__(self, serdes, taps, timeout=1024): self.reset = Signal() self.ready = Signal() self.error = Signal() @@ -171,7 +171,7 @@ class _SerdesSlaveInit(Module, AutoCSR): self.delay_max_found = delay_max_found = Signal() self.bitslip = bitslip = Signal(max=40) - timer = WaitTimer(1024) + timer = WaitTimer(timeout) self.submodules += timer self.comb += self.reset.eq(serdes.rx_idle) diff --git a/artiq/gateware/test/serwb/test_serwb_phy_init.py b/artiq/gateware/test/serwb/test_serwb_phy_init.py new file mode 100644 index 000000000..ea807f97d --- /dev/null +++ b/artiq/gateware/test/serwb/test_serwb_phy_init.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +import unittest + +from migen import * + +from artiq.gateware.serwb import packet +from artiq.gateware.serwb import etherbone +from artiq.gateware.serwb.phy import _SerdesMasterInit, _SerdesSlaveInit + + +class SerdesModel(Module): + def __init__(self, taps, mode="slave"): + self.tx_idle = Signal() + self.tx_comma = Signal() + self.rx_idle = Signal() + self.rx_comma = Signal() + + self.rx_bitslip_value = Signal(6) + self.rx_delay_rst = Signal() + self.rx_delay_inc = Signal() + self.rx_delay_ce = Signal() + + self.valid_bitslip = Signal(6) + self.valid_delays = Signal(taps) + + # # # + + delay = Signal(max=taps) + bitslip = Signal(6) + + valid_delays = Array(Signal() for i in range(taps)) + for i in range(taps): + self.comb += valid_delays[taps-1-i].eq(self.valid_delays[i]) + + self.sync += [ + bitslip.eq(self.rx_bitslip_value), + If(self.rx_delay_rst, + delay.eq(0) + ).Elif(self.rx_delay_inc & self.rx_delay_ce, + delay.eq(delay + 1) + ) + ] + + if mode == "master": + self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE")) + self.comb += self.fsm.reset.eq(self.tx_idle) + fsm.act("IDLE", + If(self.tx_comma, + NextState("SEND_COMMA") + ), + self.rx_idle.eq(1) + ) + fsm.act("SEND_COMMA", + If(valid_delays[delay] & + (bitslip == self.valid_bitslip), + self.rx_comma.eq(1) + ), + If(~self.tx_comma, + NextState("READY") + ) + ) + fsm.act("READY") + elif mode == "slave": + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + self.rx_idle.eq(1), + NextState("SEND_COMMA") + ) + fsm.act("SEND_COMMA", + If(valid_delays[delay] & + (bitslip == self.valid_bitslip), + self.rx_comma.eq(1) + ), + If(~self.tx_idle, + NextState("READY") + ) + ) + fsm.act("READY") + + +class DUTMaster(Module): + def __init__(self, taps=32): + self.submodules.serdes = SerdesModel(taps, mode="master") + self.submodules.init = _SerdesMasterInit(self.serdes, taps, timeout=1) + + +class DUTSlave(Module): + def __init__(self, taps=32): + self.submodules.serdes = SerdesModel(taps, mode="slave") + self.submodules.init = _SerdesSlaveInit(self.serdes, taps, timeout=1) + + +def generator(test, dut, valid_bitslip, valid_delays, check_success): + yield dut.serdes.valid_bitslip.eq(valid_bitslip) + yield dut.serdes.valid_delays.eq(valid_delays) + while not ((yield dut.init.ready) or + (yield dut.init.error)): + yield + if check_success: + ready = (yield dut.init.ready) + error = (yield dut.init.error) + delay_min = (yield dut.init.delay_min) + delay_max = (yield dut.init.delay_max) + delay = (yield dut.init.delay) + bitslip = (yield dut.init.bitslip) + test.assertEqual(ready, 1) + test.assertEqual(error, 0) + test.assertEqual(delay_min, 4) + test.assertEqual(delay_max, 9) + test.assertEqual(delay, 6) + test.assertEqual(bitslip, valid_bitslip) + else: + ready = (yield dut.init.ready) + error = (yield dut.init.error) + test.assertEqual(ready, 0) + test.assertEqual(error, 1) + + +class TestPHYInit(unittest.TestCase): + def test_master_init_success(self): + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b10001111100000111110000011111000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True)) + + def test_master_init_failure(self): + # partial window at the beginning + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b11000000000000000000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # partial window at the end + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b00000000000000000000000000000011 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # too small window + dut = DUTMaster() + valid_bitslip = 2 + valid_delays = 0b00000000000000010000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + + def test_slave_init_success(self): + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b10001111100000111110000011111000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True)) + + def test_slave_init_failure(self): + # partial window at the beginning + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b11000000000000000000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # partial window at the end + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b00000000000000000000000000000011 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) + # too small window + dut = DUTSlave() + valid_bitslip = 2 + valid_delays = 0b00000000000000010000000000000000 + run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False)) From a67659338d7e59764e9e45b993abea31571f5d9e Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 11:42:01 +0800 Subject: [PATCH 27/45] sayma: clean up serwb comments --- artiq/gateware/targets/sayma_amc_standalone.py | 4 ---- artiq/gateware/targets/sayma_rtm.py | 7 +------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index 8ef5cf1b8..69a3bc437 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -57,9 +57,6 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): ] # AMC/RTM serwb - # TODO: cleanup (same comments as in sayma_rtm.py) - - # serwb SERDES serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=1) self.comb += serwb_pll.refclk.eq(self.crg.cd_sys.clk) self.submodules += serwb_pll @@ -80,7 +77,6 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): serwb_phy.serdes.cd_serwb_serdes.clk, serwb_phy.serdes.cd_serwb_serdes_5x.clk) - # serwb slave serwb_core = serwb.core.SERWBCore(serwb_phy, int(self.clk_freq), mode="slave") self.submodules += serwb_core self.add_wb_slave(self.mem_map["serwb"], 8192, serwb_core.etherbone.wishbone.bus) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 797228e4e..d7906d9d7 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -69,7 +69,6 @@ CSR_RANGE_SIZE = 0x800 class SaymaRTM(Module): - def __init__(self, platform): csr_devices = [] @@ -98,10 +97,7 @@ class SaymaRTM(Module): csr_devices.append("converter_spi") self.comb += platform.request("hmc7043_reset").eq(0) - # TODO: avoid having a "serdes" clock domain at the top level, rename to "serwb_serdes" or similar. - # TODO: the above also applies to sayma_amc_standalone.py. - - # serwb SERDES + # AMC/RTM serwb serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=1) self.submodules += serwb_pll @@ -121,7 +117,6 @@ class SaymaRTM(Module): serwb_phy.serdes.cd_serwb_serdes.clk, serwb_phy.serdes.cd_serwb_serdes_5x.clk) - # serwb master serwb_core = serwb.core.SERWBCore(serwb_phy, int(clk_freq), mode="master") self.submodules += serwb_core From f765dc50deb1ab42a53f5be241b1acda21c95c2b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 11:44:33 +0800 Subject: [PATCH 28/45] sayma_rtm: do not keep DACs in reset --- artiq/gateware/targets/sayma_rtm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index d7906d9d7..11d66a137 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -88,7 +88,7 @@ class SaymaRTM(Module): ] self.comb += [ - platform.request("ad9154_rst_n").eq(0), + platform.request("ad9154_rst_n").eq(1), platform.request("ad9154_txen", 0).eq(0b11), platform.request("ad9154_txen", 1).eq(0b11) ] From ad0a940e2d9054bf283e84976217d7427bdeb70b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 11:48:54 +0800 Subject: [PATCH 29/45] sayma_rtm: hook up DAC SPI --- artiq/gateware/targets/sayma_rtm.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 11d66a137..a8449bb81 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -93,7 +93,10 @@ class SaymaRTM(Module): platform.request("ad9154_txen", 1).eq(0b11) ] - self.submodules.converter_spi = spi.SPIMaster(platform.request("hmc_spi")) + self.submodules.converter_spi = spi.SPIMaster([ + platform.request("hmc_spi"), + platform.request("ad9154_spi", 0), + platform.request("ad9154_spi", 1)]) csr_devices.append("converter_spi") self.comb += platform.request("hmc7043_reset").eq(0) From e6522212216ab24e54a76a9e48ad4c25477cdd99 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 11:50:32 +0800 Subject: [PATCH 30/45] conda: bump migen and misoc --- conda/artiq-dev/meta.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conda/artiq-dev/meta.yaml b/conda/artiq-dev/meta.yaml index 192a00fef..2e7218dc8 100644 --- a/conda/artiq-dev/meta.yaml +++ b/conda/artiq-dev/meta.yaml @@ -14,8 +14,8 @@ requirements: run: - python >=3.5.3,<3.6 - setuptools 33.1.1 - - migen 0.5.dev py_117+gite826cb9 - - misoc 0.6.dev py_35+gitd6f86c03 + - migen 0.5.dev py_143+git2e53295 + - misoc 0.6.dev py_58+git166a437a - jesd204b 0.3 - binutils-or1k-linux >=2.27 - llvm-or1k From d92cca9712a2fe94d9d5cae0b8772da8522e037d Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 12:16:52 +0800 Subject: [PATCH 31/45] artiq_flash: fix target_file handling --- artiq/frontend/artiq_flash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/frontend/artiq_flash.py b/artiq/frontend/artiq_flash.py index d6f279be0..5536565cd 100755 --- a/artiq/frontend/artiq_flash.py +++ b/artiq/frontend/artiq_flash.py @@ -82,7 +82,7 @@ class Programmer: "-s", scripts_path() ] if self.target_file is not None: - cmdline += ["-f", target_file] + cmdline += ["-f", self.target_file] cmdline += ["-c", "; ".join(self.prog)] subprocess.check_call(cmdline) From 5a041c24f32170291912674444fd4528c5048750 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 12:17:52 +0800 Subject: [PATCH 32/45] conda: bump misoc --- conda/artiq-dev/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq-dev/meta.yaml b/conda/artiq-dev/meta.yaml index 2e7218dc8..d08f13df8 100644 --- a/conda/artiq-dev/meta.yaml +++ b/conda/artiq-dev/meta.yaml @@ -15,7 +15,7 @@ requirements: - python >=3.5.3,<3.6 - setuptools 33.1.1 - migen 0.5.dev py_143+git2e53295 - - misoc 0.6.dev py_58+git166a437a + - misoc 0.6.dev py_59+git3bd95ce0 - jesd204b 0.3 - binutils-or1k-linux >=2.27 - llvm-or1k From bacf8a1614a3dfc1b608b164f9c0c38b1239f25a Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 12:52:09 +0800 Subject: [PATCH 33/45] style --- artiq/gateware/targets/kc705_drtio_master.py | 2 +- artiq/gateware/targets/kc705_drtio_satellite.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/gateware/targets/kc705_drtio_master.py b/artiq/gateware/targets/kc705_drtio_master.py index 889258965..0257b2e40 100755 --- a/artiq/gateware/targets/kc705_drtio_master.py +++ b/artiq/gateware/targets/kc705_drtio_master.py @@ -59,7 +59,7 @@ class Master(MiniSoC, AMPSoC): self.add_wb_slave(self.mem_map["drtio_aux"], 0x800, self.drtio0.aux_controller.bus) self.add_memory_region("drtio0_aux", self.mem_map["drtio_aux"] | self.shadow_base, 0x800) - self.config["has_drtio"] = None + self.config["HAS_DRTIO"] = None self.add_csr_group("drtio", ["drtio0"]) self.add_memory_group("drtio_aux", ["drtio0_aux"]) diff --git a/artiq/gateware/targets/kc705_drtio_satellite.py b/artiq/gateware/targets/kc705_drtio_satellite.py index b4579d50f..321f7b00a 100755 --- a/artiq/gateware/targets/kc705_drtio_satellite.py +++ b/artiq/gateware/targets/kc705_drtio_satellite.py @@ -64,7 +64,7 @@ class Satellite(BaseSoC): self.add_wb_slave(self.mem_map["drtio_aux"], 0x800, self.drtio0.aux_controller.bus) self.add_memory_region("drtio0_aux", self.mem_map["drtio_aux"] | self.shadow_base, 0x800) - self.config["has_drtio"] = None + self.config["HAS_DRTIO"] = None self.add_csr_group("drtio", ["drtio0"]) self.add_memory_group("drtio_aux", ["drtio0_aux"]) From a4144a07c44d62c2a08906011da9b04e57ae98cf Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 13:04:38 +0800 Subject: [PATCH 34/45] sayma_amc: add converter SPI config defines --- artiq/gateware/targets/sayma_amc_standalone.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index 69a3bc437..026fc1159 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -140,6 +140,11 @@ def main(): args.rtm_csr_csv) for name, origin, busword, csrs in remote_csr_regions: soc.add_csr_region(name, origin, busword, csrs) + # Configuration for RTM peripherals. Keep in sync with sayma_rtm.py! + soc.config["HAS_HMC830_7043"] = None + soc.config["CONVERTER_SPI_HMC830_CS"] = 0 + soc.config["CONVERTER_SPI_HMC7043_CS"] = 1 + soc.config["CONVERTER_SPI_FIRST_AD9154_CS"] = 2 build_artiq_soc(soc, builder_argdict(args)) From 0a5904bbaa2163ffc1703435bd5cc996f04ecae5 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 13:05:48 +0800 Subject: [PATCH 35/45] firmware: support for multiple JESD DACs --- artiq/firmware/libboard/ad9154.rs | 69 +++++++++++++++---------------- artiq/firmware/libboard/ad9516.rs | 2 +- artiq/gateware/targets/phaser.py | 18 ++++---- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/artiq/firmware/libboard/ad9154.rs b/artiq/firmware/libboard/ad9154.rs index 69f5ef547..db74f924b 100644 --- a/artiq/firmware/libboard/ad9154.rs +++ b/artiq/firmware/libboard/ad9154.rs @@ -2,7 +2,7 @@ use csr; use clock; use ad9154_reg; -fn spi_setup() { +fn spi_setup(dacno: u8) { unsafe { csr::converter_spi::offline_write(1); csr::converter_spi::cs_polarity_write(0); @@ -14,7 +14,7 @@ fn spi_setup() { csr::converter_spi::clk_div_read_write(16); csr::converter_spi::xfer_len_write_write(24); csr::converter_spi::xfer_len_read_write(0); - csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_DAC_CS); + csr::converter_spi::cs_write(1 << (csr::CONFIG_CONVERTER_SPI_FIRST_AD9154_CS + dacno as u32)); csr::converter_spi::offline_write(0); } } @@ -35,39 +35,39 @@ fn read(addr: u16) -> u8 { } } -fn jesd_reset(rst: bool) { +fn jesd_reset(dacno: u8, rst: bool) { unsafe { - csr::ad9154::jesd_jreset_write(if rst {1} else {0}) + (csr::AD9154[dacno as usize].jesd_jreset_write)(if rst {1} else {0}) } } -fn jesd_enable(en: bool) { +fn jesd_enable(dacno: u8, en: bool) { unsafe { - csr::ad9154::jesd_control_enable_write(if en {1} else {0}) + (csr::AD9154[dacno as usize].jesd_control_enable_write)(if en {1} else {0}) } } -fn jesd_ready() -> bool { +fn jesd_ready(dacno: u8) -> bool { unsafe { - csr::ad9154::jesd_control_ready_read() != 0 + (csr::AD9154[dacno as usize].jesd_control_ready_read)() != 0 } } -fn jesd_prbs(en: bool) { +fn jesd_prbs(dacno: u8, en: bool) { unsafe { - csr::ad9154::jesd_control_prbs_config_write(if en {1} else {0}) + (csr::AD9154[dacno as usize].jesd_control_prbs_config_write)(if en {1} else {0}) } } -fn jesd_stpl(en: bool) { +fn jesd_stpl(dacno: u8, en: bool) { unsafe { - csr::ad9154::jesd_control_stpl_enable_write(if en {1} else {0}) + (csr::AD9154[dacno as usize].jesd_control_stpl_enable_write)(if en {1} else {0}) } } -fn jesd_jsync() -> bool { +fn jesd_jsync(dacno: u8) -> bool { unsafe { - csr::ad9154::jesd_control_jsync_read() != 0 + (csr::AD9154[dacno as usize].jesd_control_jsync_read)() != 0 } } @@ -421,19 +421,24 @@ fn monitor() { write(ad9154_reg::IRQ_STATUS3, 0x00); } -fn cfg() -> Result<(), &'static str> { - jesd_enable(false); - jesd_prbs(false); - jesd_stpl(false); +fn cfg(dacno: u8) -> Result<(), &'static str> { + spi_setup(dacno); + // Release the JESD clock domain reset late, as we need to + // set up clock chips before. + jesd_reset(dacno, false); + + jesd_enable(dacno, false); + jesd_prbs(dacno, false); + jesd_stpl(dacno, false); clock::spin_us(10000); - jesd_enable(true); + jesd_enable(dacno, true); dac_setup()?; - jesd_enable(false); + jesd_enable(dacno, false); clock::spin_us(10000); - jesd_enable(true); + jesd_enable(dacno, true); monitor(); let t = clock::get_ms(); - while !jesd_ready() { + while !jesd_ready(dacno) { if clock::get_ms() > t + 200 { return Err("JESD ready timeout"); } @@ -442,7 +447,7 @@ fn cfg() -> Result<(), &'static str> { if read(ad9154_reg::CODEGRPSYNCFLG) != 0x0f { return Err("bad CODEGRPSYNCFLG") } - if !jesd_jsync() { + if !jesd_jsync(dacno) { return Err("bad SYNC") } if read(ad9154_reg::FRAMESYNCFLG) != 0x0f { @@ -458,18 +463,10 @@ fn cfg() -> Result<(), &'static str> { } pub fn init() -> Result<(), &'static str> { - spi_setup(); - - // Release the JESD clock domain reset late, as we need to - // set up clock chips before. - jesd_reset(false); - - for i in 0..99 { - let outcome = cfg(); - match outcome { - Ok(_) => return outcome, - Err(e) => warn!("config attempt #{} failed ({}), retrying", i, e) - } + for dacno in 0..csr::AD9154.len() { + let dacno = dacno as u8; + debug!("setting up DAC #{}", dacno); + cfg(dacno)?; } - cfg() + Ok(()) } diff --git a/artiq/firmware/libboard/ad9516.rs b/artiq/firmware/libboard/ad9516.rs index e6f582050..93d3e1e1e 100644 --- a/artiq/firmware/libboard/ad9516.rs +++ b/artiq/firmware/libboard/ad9516.rs @@ -13,7 +13,7 @@ fn spi_setup() { csr::converter_spi::clk_div_read_write(16); csr::converter_spi::xfer_len_write_write(24); csr::converter_spi::xfer_len_read_write(0); - csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_CLK_CS); + csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_AD9516_CS); csr::converter_spi::offline_write(0); } } diff --git a/artiq/gateware/targets/phaser.py b/artiq/gateware/targets/phaser.py index fe141837f..735604063 100755 --- a/artiq/gateware/targets/phaser.py +++ b/artiq/gateware/targets/phaser.py @@ -192,12 +192,14 @@ class Phaser(MiniSoC, AMPSoC): self.comb += ad9154_spi.en.eq(1) self.submodules.converter_spi = spi_csr.SPIMaster(ad9154_spi) self.csr_devices.append("converter_spi") - self.config["CONVERTER_SPI_DAC_CS"] = 0 - self.config["CONVERTER_SPI_CLK_CS"] = 1 self.config["HAS_AD9516"] = None + self.config["CONVERTER_SPI_AD9516_CS"] = 1 + self.config["CONVERTER_SPI_FIRST_AD9154_CS"] = 0 - self.submodules.ad9154 = AD9154(platform) - self.csr_devices.append("ad9154") + self.submodules.ad9154_0 = AD9154(platform) + self.csr_devices.append("ad9154_0") + self.config["HAS_AD9154"] = None + self.add_csr_group("ad9154", ["ad9154_0"]) rtio_channels = [] @@ -218,7 +220,7 @@ class Phaser(MiniSoC, AMPSoC): self.config["RTIO_FIRST_SAWG_CHANNEL"] = len(rtio_channels) rtio_channels.extend(rtio.Channel.from_phy(phy) - for sawg in self.ad9154.sawgs + for sawg in self.ad9154_0.sawgs for phy in sawg.phys) self.config["HAS_RTIO_LOG"] = None @@ -226,7 +228,7 @@ class Phaser(MiniSoC, AMPSoC): rtio_channels.append(rtio.LogChannel()) self.submodules.rtio_crg = _PhaserCRG( - platform, self.ad9154.jesd.cd_jesd.clk) + platform, self.ad9154_0.jesd.cd_jesd.clk) self.csr_devices.append("rtio_crg") self.submodules.rtio_core = rtio.Core(rtio_channels) self.csr_devices.append("rtio_core") @@ -248,8 +250,8 @@ class Phaser(MiniSoC, AMPSoC): platform.add_false_path_constraints( self.crg.cd_sys.clk, self.rtio_crg.cd_rtio.clk) platform.add_false_path_constraints( - self.crg.cd_sys.clk, self.ad9154.jesd.cd_jesd.clk) - for phy in self.ad9154.jesd.phys: + self.crg.cd_sys.clk, self.ad9154_0.jesd.cd_jesd.clk) + for phy in self.ad9154_0.jesd.phys: platform.add_false_path_constraints( self.crg.cd_sys.clk, phy.transmitter.cd_tx.clk) From 9edff2c5205c43893faaf50eb0dad2781cf9a770 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 13:34:48 +0800 Subject: [PATCH 36/45] remote_csr: interpret length as CSR size, not number of bus words --- artiq/gateware/remote_csr.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/artiq/gateware/remote_csr.py b/artiq/gateware/remote_csr.py index 32644b8f9..7acbba798 100644 --- a/artiq/gateware/remote_csr.py +++ b/artiq/gateware/remote_csr.py @@ -21,6 +21,7 @@ def _get_csr_data(csv_file): def get_remote_csr_regions(offset, csv_file): + busword = 32 regions = [] for region_name, csrs_info in _get_csr_data(csv_file).items(): csrs_info = sorted(csrs_info, key=itemgetter(1)) @@ -30,11 +31,12 @@ def get_remote_csr_regions(offset, csv_file): for csr_name, address, length, ro in csrs_info: if address != next_address: raise ValueError("CSRs are not contiguous") - next_address += 4*length + nr = (length + busword - 1)//busword + next_address += nr*busword//8 if ro: - csr = CSRStatus(32*length, name=csr_name) + csr = CSRStatus(length, name=csr_name) else: - csr = CSRStorage(32*length, name=csr_name) + csr = CSRStorage(length, name=csr_name) csrs.append(csr) - regions.append((region_name, offset + origin, 32, csrs)) + regions.append((region_name, offset + origin, busword, csrs)) return regions From 44edba0c654b48e328cf75751c40eb7860903eb5 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 13:35:47 +0800 Subject: [PATCH 37/45] firmware: add placeholder code for HMC830/7043 initialization --- artiq/firmware/libboard/hmc830_7043.rs | 4 ++++ artiq/firmware/libboard/lib.rs | 2 ++ artiq/firmware/runtime/lib.rs | 6 ++++-- artiq/firmware/satman/lib.rs | 6 ++++-- 4 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 artiq/firmware/libboard/hmc830_7043.rs diff --git a/artiq/firmware/libboard/hmc830_7043.rs b/artiq/firmware/libboard/hmc830_7043.rs new file mode 100644 index 000000000..9d2c20b4a --- /dev/null +++ b/artiq/firmware/libboard/hmc830_7043.rs @@ -0,0 +1,4 @@ +pub fn init() -> Result<(), &'static str> { + error!("HMC830/7043 support is not implemented"); + Ok(()) +} diff --git a/artiq/firmware/libboard/lib.rs b/artiq/firmware/libboard/lib.rs index d0c6ecc77..edeef1e69 100644 --- a/artiq/firmware/libboard/lib.rs +++ b/artiq/firmware/libboard/lib.rs @@ -30,6 +30,8 @@ pub mod si5324; mod ad9516_reg; #[cfg(has_ad9516)] pub mod ad9516; +#[cfg(has_hmc830_7043)] +pub mod hmc830_7043; #[cfg(has_ad9154)] #[allow(dead_code)] mod ad9154_reg; diff --git a/artiq/firmware/runtime/lib.rs b/artiq/firmware/runtime/lib.rs index f679eba28..80b780497 100644 --- a/artiq/firmware/runtime/lib.rs +++ b/artiq/firmware/runtime/lib.rs @@ -78,9 +78,11 @@ fn startup() { #[cfg(has_i2c)] board::i2c::init(); #[cfg(has_ad9516)] - board::ad9516::init().expect("cannot initialize ad9516"); + board::ad9516::init().expect("cannot initialize AD9516"); + #[cfg(has_hmc830_7043)] + board::hmc830_7043::init().expect("cannot initialize HMC830/7043"); #[cfg(has_ad9154)] - board::ad9154::init().expect("cannot initialize ad9154"); + board::ad9154::init().expect("cannot initialize AD9154"); let hardware_addr; match config::read_str("mac", |r| r?.parse()) { diff --git a/artiq/firmware/satman/lib.rs b/artiq/firmware/satman/lib.rs index 5ffb0d6a2..3137e7e04 100644 --- a/artiq/firmware/satman/lib.rs +++ b/artiq/firmware/satman/lib.rs @@ -196,9 +196,11 @@ fn startup() { info!("gateware version {}", board::ident(&mut [0; 64])); #[cfg(has_ad9516)] - board::ad9516::init().expect("cannot initialize ad9516"); + board::ad9516::init().expect("cannot initialize AD9516"); + #[cfg(has_hmc830_7043)] + board::hmc830_7043::init().expect("cannot initialize HMC830/7043"); board::i2c::init(); - board::si5324::setup(&SI5324_SETTINGS).expect("cannot initialize si5324"); + board::si5324::setup(&SI5324_SETTINGS).expect("cannot initialize Si5324"); loop { while !drtio_link_is_up() { From b609366c6fd2ccb22b3184d594095c4bac52bdee Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 13:42:32 +0800 Subject: [PATCH 38/45] runtime: fix Rust types in RTIO Previous code assumed all RTIO registers were u32, but this was changed by misoc c5edcd08. --- artiq/firmware/ksupport/rtio.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/artiq/firmware/ksupport/rtio.rs b/artiq/firmware/ksupport/rtio.rs index ddbd38afc..ebc86b9f5 100644 --- a/artiq/firmware/ksupport/rtio.rs +++ b/artiq/firmware/ksupport/rtio.rs @@ -4,12 +4,12 @@ use board::csr; use ::send; use kernel_proto::*; -pub const RTIO_O_STATUS_WAIT: u32 = 1; -pub const RTIO_O_STATUS_UNDERFLOW: u32 = 2; -pub const RTIO_O_STATUS_SEQUENCE_ERROR: u32 = 4; -pub const RTIO_I_STATUS_WAIT_EVENT: u32 = 1; -pub const RTIO_I_STATUS_OVERFLOW: u32 = 2; -pub const RTIO_I_STATUS_WAIT_STATUS: u32 = 4; +pub const RTIO_O_STATUS_WAIT: u8 = 1; +pub const RTIO_O_STATUS_UNDERFLOW: u8 = 2; +pub const RTIO_O_STATUS_SEQUENCE_ERROR: u8 = 4; +pub const RTIO_I_STATUS_WAIT_EVENT: u8 = 1; +pub const RTIO_I_STATUS_OVERFLOW: u8 = 2; +pub const RTIO_I_STATUS_WAIT_STATUS: u8 = 4; pub extern fn init() { send(&RtioInitRequest); @@ -36,7 +36,7 @@ pub unsafe fn rtio_i_data_read(offset: usize) -> u32 { } #[inline(never)] -unsafe fn process_exceptional_status(timestamp: i64, channel: i32, status: u32) { +unsafe fn process_exceptional_status(timestamp: i64, channel: i32, status: u8) { if status & RTIO_O_STATUS_WAIT != 0 { while csr::rtio::o_status_read() & RTIO_O_STATUS_WAIT != 0 {} } @@ -54,11 +54,11 @@ unsafe fn process_exceptional_status(timestamp: i64, channel: i32, status: u32) pub extern fn output(timestamp: i64, channel: i32, addr: i32, data: i32) { unsafe { - csr::rtio::chan_sel_write(channel as u32); + csr::rtio::chan_sel_write(channel as _); // writing timestamp clears o_data csr::rtio::timestamp_write(timestamp as u64); - csr::rtio::o_address_write(addr as u32); - rtio_o_data_write(0, data as u32); + csr::rtio::o_address_write(addr as _); + rtio_o_data_write(0, data as _); csr::rtio::o_we_write(1); let status = csr::rtio::o_status_read(); if status != 0 { @@ -69,12 +69,12 @@ pub extern fn output(timestamp: i64, channel: i32, addr: i32, data: i32) { pub extern fn output_wide(timestamp: i64, channel: i32, addr: i32, data: CSlice) { unsafe { - csr::rtio::chan_sel_write(channel as u32); + csr::rtio::chan_sel_write(channel as _); // writing timestamp clears o_data csr::rtio::timestamp_write(timestamp as u64); - csr::rtio::o_address_write(addr as u32); + csr::rtio::o_address_write(addr as _); for i in 0..data.len() { - rtio_o_data_write(i, data[i] as u32) + rtio_o_data_write(i, data[i] as _) } csr::rtio::o_we_write(1); let status = csr::rtio::o_status_read(); @@ -86,7 +86,7 @@ pub extern fn output_wide(timestamp: i64, channel: i32, addr: i32, data: CSlice< pub extern fn input_timestamp(timeout: i64, channel: i32) -> u64 { unsafe { - csr::rtio::chan_sel_write(channel as u32); + csr::rtio::chan_sel_write(channel as _); csr::rtio::timestamp_write(timeout as u64); csr::rtio::i_request_write(1); @@ -110,7 +110,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> u64 { pub extern fn input_data(channel: i32) -> i32 { unsafe { - csr::rtio::chan_sel_write(channel as u32); + csr::rtio::chan_sel_write(channel as _); csr::rtio::timestamp_write(0xffffffff_ffffffff); csr::rtio::i_request_write(1); From c5fe2799cf4ce1d1cd3324e459fc81cdee492143 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 31 Aug 2017 13:44:31 +0800 Subject: [PATCH 39/45] conda: bump misoc --- conda/artiq-dev/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq-dev/meta.yaml b/conda/artiq-dev/meta.yaml index d08f13df8..3baaaaaf7 100644 --- a/conda/artiq-dev/meta.yaml +++ b/conda/artiq-dev/meta.yaml @@ -15,7 +15,7 @@ requirements: - python >=3.5.3,<3.6 - setuptools 33.1.1 - migen 0.5.dev py_143+git2e53295 - - misoc 0.6.dev py_59+git3bd95ce0 + - misoc 0.6.dev py_62+git7d59ec36 - jesd204b 0.3 - binutils-or1k-linux >=2.27 - llvm-or1k From 091bb280433a66049ccab4bdfed2f7cd4c387141 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 5 Sep 2017 21:13:04 +0800 Subject: [PATCH 40/45] libboard: use libbuild_artiq --- artiq/firmware/libboard/Cargo.toml | 3 +++ artiq/firmware/libboard/build.rs | 14 ++------------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/artiq/firmware/libboard/Cargo.toml b/artiq/firmware/libboard/Cargo.toml index 950fbcff2..e3fed9298 100644 --- a/artiq/firmware/libboard/Cargo.toml +++ b/artiq/firmware/libboard/Cargo.toml @@ -8,6 +8,9 @@ build = "build.rs" name = "board" path = "lib.rs" +[build-dependencies] +build_artiq = { path = "../libbuild_artiq" } + [dependencies] log = { version = "0.3", default-features = false } diff --git a/artiq/firmware/libboard/build.rs b/artiq/firmware/libboard/build.rs index a7b0335e5..84aa97652 100644 --- a/artiq/firmware/libboard/build.rs +++ b/artiq/firmware/libboard/build.rs @@ -1,15 +1,5 @@ -use std::env; -use std::path::Path; -use std::io::{BufRead, BufReader}; -use std::fs::File; +extern crate build_artiq; fn main() { - let out_dir = env::var("BUILDINC_DIRECTORY").unwrap(); - let cfg_path = Path::new(&out_dir).join("generated").join("rust-cfg"); - println!("cargo:rerun-if-changed={}", cfg_path.to_str().unwrap()); - - let f = BufReader::new(File::open(&cfg_path).unwrap()); - for line in f.lines() { - println!("cargo:rustc-cfg={}", line.unwrap()); - } + build_artiq::misoc_cfg(); } From 4baf17cebe94e2348d3d18727fd653e04bbab31b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Tue, 5 Sep 2017 21:46:03 +0800 Subject: [PATCH 41/45] libboard: generate HMC7043 register write list --- artiq/firmware/libboard/build.rs | 24 + artiq/firmware/libboard/hmc7043_gen_writes.py | 30 + .../libboard/hmc7043_guiexport_10gbps.py | 697 ++++++++++++++++++ artiq/firmware/libboard/hmc830_7043.rs | 2 + 4 files changed, 753 insertions(+) create mode 100755 artiq/firmware/libboard/hmc7043_gen_writes.py create mode 100644 artiq/firmware/libboard/hmc7043_guiexport_10gbps.py diff --git a/artiq/firmware/libboard/build.rs b/artiq/firmware/libboard/build.rs index 84aa97652..26a3aa1bc 100644 --- a/artiq/firmware/libboard/build.rs +++ b/artiq/firmware/libboard/build.rs @@ -1,5 +1,29 @@ extern crate build_artiq; +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +use std::process::Command; + +fn gen_hmc7043_writes() { + println!("cargo:rerun-if-changed=hmc7043_gen_writes.py"); + println!("cargo:rerun-if-changed=hmc7043_guiexport_10gbps.py"); + + let hmc7043_writes = + Command::new("python3") + .arg("hmc7043_gen_writes.py") + .arg("hmc7043_guiexport_10gbps.py") + .output() + .ok() + .and_then(|o| String::from_utf8(o.stdout).ok()) + .unwrap(); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let mut f = File::create(out_dir.join("hmc7043_writes.rs")).unwrap(); + write!(f, "{}", hmc7043_writes).unwrap(); +} + fn main() { build_artiq::misoc_cfg(); + gen_hmc7043_writes(); } diff --git a/artiq/firmware/libboard/hmc7043_gen_writes.py b/artiq/firmware/libboard/hmc7043_gen_writes.py new file mode 100755 index 000000000..3f0a8cb47 --- /dev/null +++ b/artiq/firmware/libboard/hmc7043_gen_writes.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +# The HMC7043 GUI exports register write lists into Python files. +# This script converts them into Rust arrays. + +import sys +import runpy + + +class DUT: + def __init__(self): + self.writes = [] + + def write(self, address, value): + self.writes.append((address, value)) + + +def main(): + dut = DUT() + runpy.run_path(sys.argv[1], {"dut": dut}) + + print("// This file was autogenerated by hmc7043_gen_writes.py") + print("const HMC7043_WRITES: [(u16, u8); {}] = [".format(len(dut.writes))) + for address, value in dut.writes: + print(" (0x{:04x}, 0x{:02x}),".format(address, value)) + print("];") + + +if __name__ == "__main__": + main() diff --git a/artiq/firmware/libboard/hmc7043_guiexport_10gbps.py b/artiq/firmware/libboard/hmc7043_guiexport_10gbps.py new file mode 100644 index 000000000..de3275ba0 --- /dev/null +++ b/artiq/firmware/libboard/hmc7043_guiexport_10gbps.py @@ -0,0 +1,697 @@ +# glbl_cfg1_swrst[0:0] = 0x0 +dut.write(0x0, 0x0) + +# glbl_cfg1_sleep[0:0] = 0x0 +# glbl_cfg1_restart[1:1] = 0x0 +# sysr_cfg1_pulsor_req[2:2] = 0x0 +# grpx_cfg1_mute[3:3] = 0x0 +# dist_cfg1_perf_floor[6:6] = 0x0 +# sysr_cfg1_reseed_req[7:7] = 0x0 +dut.write(0x1, 0x0) + +# sysr_cfg1_rev[0:0] = 0x0 +# sysr_cfg1_slipN_req[1:1] = 0x0 +dut.write(0x2, 0x0) + +# glbl_cfg1_ena_sysr[2:2] = 0x1 +# glbl_cfg2_ena_vcos[4:3] = 0x0 +# glbl_cfg1_ena_sysri[5:5] = 0x1 +dut.write(0x3, 0x24) + +# glbl_cfg7_ena_clkgr[6:0] = 0x3B +dut.write(0x4, 0x3B) + +# glbl_cfg1_clear_alarms[0:0] = 0x0 +dut.write(0x6, 0x0) + +# glbl_reserved[0:0] = 0x0 +dut.write(0x7, 0x0) + +# glbl_cfg5_ibuf0_en[0:0] = 0x0 +# glbl_cfg5_ibuf0_mode[4:1] = 0x7 +dut.write(0xA, 0xE) + +# glbl_cfg5_ibuf1_en[0:0] = 0x1 +# glbl_cfg5_ibuf1_mode[4:1] = 0x7 +dut.write(0xB, 0xF) + +# glbl_cfg5_gpi1_en[0:0] = 0x0 +# glbl_cfg5_gpi1_sel[4:1] = 0x0 +dut.write(0x46, 0x0) + +# glbl_cfg8_gpo1_en[0:0] = 0x1 +# glbl_cfg8_gpo1_mode[1:1] = 0x1 +# glbl_cfg8_gpo1_sel[7:2] = 0x7 +dut.write(0x50, 0x1F) + +# glbl_cfg2_sdio_en[0:0] = 0x1 +# glbl_cfg2_sdio_mode[1:1] = 0x1 +dut.write(0x54, 0x3) + +# sysr_cfg3_pulsor_mode[2:0] = 0x1 +dut.write(0x5A, 0x1) + +# sysr_cfg1_synci_invpol[0:0] = 0x0 +# sysr_cfg1_ext_sync_retimemode[2:2] = 0x1 +dut.write(0x5B, 0x4) + +# sysr_cfg16_divrat_lsb[7:0] = 0x0 +dut.write(0x5C, 0x0) + +# sysr_cfg16_divrat_msb[3:0] = 0x6 +dut.write(0x5D, 0x6) + +# dist_cfg1_extvco_islowfreq_sel[0:0] = 0x0 +# dist_cfg1_extvco_div2_sel[1:1] = 0x1 +dut.write(0x64, 0x2) + +# clkgrpx_cfg1_alg_dly_lowpwr_sel[0:0] = 0x0 +dut.write(0x65, 0x0) + +# alrm_cfg1_sysr_unsyncd_allow[1:1] = 0x0 +# alrm_cfg1_clkgrpx_validph_allow[2:2] = 0x0 +# alrm_cfg1_sync_req_allow[4:4] = 0x1 +dut.write(0x71, 0x10) + +# glbl_ro8_chipid_lob[7:0] = 0x1 +dut.write(0x78, 0x1) + +# glbl_ro8_chipid_mid[7:0] = 0x52 +dut.write(0x79, 0x52) + +# glbl_ro8_chipid_hib[7:0] = 0x4 +dut.write(0x7A, 0x4) + +# alrm_ro1_sysr_unsyncd_now[1:1] = 0x1 +# alrm_ro1_clkgrpx_validph_now[2:2] = 0x0 +# alrm_ro1_sync_req_now[4:4] = 0x1 +dut.write(0x7D, 0x12) + +# sysr_ro4_fsmstate[3:0] = 0x2 +# grpx_ro1_outdivfsm_busy[4:4] = 0x0 +dut.write(0x91, 0x2) + +# reg_98[7:0] = 0x0 +dut.write(0x98, 0x0) + +# reg_99[7:0] = 0x0 +dut.write(0x99, 0x0) + +# reg_9A[7:0] = 0x0 +dut.write(0x9A, 0x0) + +# reg_9B[7:0] = 0xAA +dut.write(0x9B, 0xAA) + +# reg_9C[7:0] = 0xAA +dut.write(0x9C, 0xAA) + +# reg_9D[7:0] = 0xAA +dut.write(0x9D, 0xAA) + +# reg_9E[7:0] = 0xAA +dut.write(0x9E, 0xAA) + +# reg_9F[7:0] = 0x4D +dut.write(0x9F, 0x4D) + +# reg_A0[7:0] = 0xDF +dut.write(0xA0, 0xDF) + +# reg_A1[7:0] = 0x97 +dut.write(0xA1, 0x97) + +# reg_A2[7:0] = 0x3 +dut.write(0xA2, 0x3) + +# reg_A3[7:0] = 0x0 +dut.write(0xA3, 0x0) + +# reg_A4[7:0] = 0x0 +dut.write(0xA4, 0x0) + +# reg_AD[7:0] = 0x0 +dut.write(0xAD, 0x0) + +# reg_AE[7:0] = 0x8 +dut.write(0xAE, 0x8) + +# reg_AF[7:0] = 0x50 +dut.write(0xAF, 0x50) + +# reg_B0[7:0] = 0x4 +dut.write(0xB0, 0x4) + +# reg_B1[7:0] = 0xD +dut.write(0xB1, 0xD) + +# reg_B2[7:0] = 0x0 +dut.write(0xB2, 0x0) + +# reg_B3[7:0] = 0x0 +dut.write(0xB3, 0x0) + +# reg_B5[7:0] = 0x0 +dut.write(0xB5, 0x0) + +# reg_B6[7:0] = 0x0 +dut.write(0xB6, 0x0) + +# reg_B7[7:0] = 0x0 +dut.write(0xB7, 0x0) + +# reg_B8[7:0] = 0x0 +dut.write(0xB8, 0x0) + +# clkgrp1_div1_cfg1_en[0:0] = 0x1 +# clkgrp1_div1_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp1_div1_cfg2_startmode[3:2] = 0x0 +# clkgrp1_div1_cfg1_rev[4:4] = 0x1 +# clkgrp1_div1_cfg1_slipmask[5:5] = 0x1 +# clkgrp1_div1_cfg1_reseedmask[6:6] = 0x1 +# clkgrp1_div1_cfg1_hi_perf[7:7] = 0x0 +dut.write(0xC8, 0x73) + +# clkgrp1_div1_cfg12_divrat_lsb[7:0] = 0x1 +dut.write(0xC9, 0x1) + +# clkgrp1_div1_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0xCA, 0x0) + +# clkgrp1_div1_cfg5_fine_delay[4:0] = 0x0 +dut.write(0xCB, 0x0) + +# clkgrp1_div1_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0xCC, 0x0) + +# clkgrp1_div1_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0xCD, 0x0) + +# clkgrp1_div1_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0xCE, 0x0) + +# clkgrp1_div1_cfg2_sel_outmux[1:0] = 0x3 +# clkgrp1_div1_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0xCF, 0x3) + +# clkgrp1_div1_cfg5_drvr_res[1:0] = 0x0 +# clkgrp1_div1_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp1_div1_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp1_div1_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp1_div1_cfg2_mutesel[7:6] = 0x0 +dut.write(0xD0, 0x8) + +# clkgrp1_div2_cfg1_en[0:0] = 0x1 +# clkgrp1_div2_cfg1_phdelta_mslip[1:1] = 0x0 +# clkgrp1_div2_cfg2_startmode[3:2] = 0x0 +# clkgrp1_div2_cfg1_rev[4:4] = 0x1 +# clkgrp1_div2_cfg1_slipmask[5:5] = 0x1 +# clkgrp1_div2_cfg1_reseedmask[6:6] = 0x1 +# clkgrp1_div2_cfg1_hi_perf[7:7] = 0x0 +dut.write(0xD2, 0x71) + +# clkgrp1_div2_cfg12_divrat_lsb[7:0] = 0x40 +dut.write(0xD3, 0x40) + +# clkgrp1_div2_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0xD4, 0x0) + +# clkgrp1_div2_cfg5_fine_delay[4:0] = 0x0 +dut.write(0xD5, 0x0) + +# clkgrp1_div2_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0xD6, 0x0) + +# clkgrp1_div2_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0xD7, 0x0) + +# clkgrp1_div2_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0xD8, 0x0) + +# clkgrp1_div2_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp1_div2_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0xD9, 0x0) + +# clkgrp1_div2_cfg5_drvr_res[1:0] = 0x1 +# clkgrp1_div2_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp1_div2_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp1_div2_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp1_div2_cfg2_mutesel[7:6] = 0x0 +dut.write(0xDA, 0x9) + +# clkgrp2_div1_cfg1_en[0:0] = 0x1 +# clkgrp2_div1_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp2_div1_cfg2_startmode[3:2] = 0x0 +# clkgrp2_div1_cfg1_rev[4:4] = 0x1 +# clkgrp2_div1_cfg1_slipmask[5:5] = 0x1 +# clkgrp2_div1_cfg1_reseedmask[6:6] = 0x1 +# clkgrp2_div1_cfg1_hi_perf[7:7] = 0x0 +dut.write(0xDC, 0x73) + +# clkgrp2_div1_cfg12_divrat_lsb[7:0] = 0x1 +dut.write(0xDD, 0x1) + +# clkgrp2_div1_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0xDE, 0x0) + +# clkgrp2_div1_cfg5_fine_delay[4:0] = 0x0 +dut.write(0xDF, 0x0) + +# clkgrp2_div1_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0xE0, 0x0) + +# clkgrp2_div1_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0xE1, 0x0) + +# clkgrp2_div1_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0xE2, 0x0) + +# clkgrp2_div1_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp2_div1_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0xE3, 0x0) + +# clkgrp2_div1_cfg5_drvr_res[1:0] = 0x0 +# clkgrp2_div1_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp2_div1_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp2_div1_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp2_div1_cfg2_mutesel[7:6] = 0x0 +dut.write(0xE4, 0x8) + +# clkgrp2_div2_cfg1_en[0:0] = 0x1 +# clkgrp2_div2_cfg1_phdelta_mslip[1:1] = 0x0 +# clkgrp2_div2_cfg2_startmode[3:2] = 0x0 +# clkgrp2_div2_cfg1_rev[4:4] = 0x1 +# clkgrp2_div2_cfg1_slipmask[5:5] = 0x1 +# clkgrp2_div2_cfg1_reseedmask[6:6] = 0x1 +# clkgrp2_div2_cfg1_hi_perf[7:7] = 0x0 +dut.write(0xE6, 0x71) + +# clkgrp2_div2_cfg12_divrat_lsb[7:0] = 0x40 +dut.write(0xE7, 0x40) + +# clkgrp2_div2_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0xE8, 0x0) + +# clkgrp2_div2_cfg5_fine_delay[4:0] = 0x0 +dut.write(0xE9, 0x0) + +# clkgrp2_div2_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0xEA, 0x0) + +# clkgrp2_div2_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0xEB, 0x0) + +# clkgrp2_div2_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0xEC, 0x0) + +# clkgrp2_div2_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp2_div2_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0xED, 0x0) + +# clkgrp2_div2_cfg5_drvr_res[1:0] = 0x1 +# clkgrp2_div2_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp2_div2_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp2_div2_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp2_div2_cfg2_mutesel[7:6] = 0x0 +dut.write(0xEE, 0x9) + +# clkgrp3_div1_cfg1_en[0:0] = 0x0 +# clkgrp3_div1_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp3_div1_cfg2_startmode[3:2] = 0x0 +# clkgrp3_div1_cfg1_rev[4:4] = 0x1 +# clkgrp3_div1_cfg1_slipmask[5:5] = 0x1 +# clkgrp3_div1_cfg1_reseedmask[6:6] = 0x1 +# clkgrp3_div1_cfg1_hi_perf[7:7] = 0x0 +dut.write(0xF0, 0x72) + +# clkgrp3_div1_cfg12_divrat_lsb[7:0] = 0x2 +dut.write(0xF1, 0x2) + +# clkgrp3_div1_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0xF2, 0x0) + +# clkgrp3_div1_cfg5_fine_delay[4:0] = 0x0 +dut.write(0xF3, 0x0) + +# clkgrp3_div1_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0xF4, 0x0) + +# clkgrp3_div1_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0xF5, 0x0) + +# clkgrp3_div1_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0xF6, 0x0) + +# clkgrp3_div1_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp3_div1_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0xF7, 0x0) + +# clkgrp3_div1_cfg5_drvr_res[1:0] = 0x0 +# clkgrp3_div1_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp3_div1_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp3_div1_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp3_div1_cfg2_mutesel[7:6] = 0x0 +dut.write(0xF8, 0x8) + +# clkgrp3_div2_cfg1_en[0:0] = 0x0 +# clkgrp3_div2_cfg1_phdelta_mslip[1:1] = 0x0 +# clkgrp3_div2_cfg2_startmode[3:2] = 0x0 +# clkgrp3_div2_cfg1_rev[4:4] = 0x1 +# clkgrp3_div2_cfg1_slipmask[5:5] = 0x1 +# clkgrp3_div2_cfg1_reseedmask[6:6] = 0x1 +# clkgrp3_div2_cfg1_hi_perf[7:7] = 0x0 +dut.write(0xFA, 0x70) + +# clkgrp3_div2_cfg12_divrat_lsb[7:0] = 0x80 +dut.write(0xFB, 0x80) + +# clkgrp3_div2_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0xFC, 0x0) + +# clkgrp3_div2_cfg5_fine_delay[4:0] = 0x0 +dut.write(0xFD, 0x0) + +# clkgrp3_div2_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0xFE, 0x0) + +# clkgrp3_div2_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0xFF, 0x0) + +# clkgrp3_div2_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x100, 0x0) + +# clkgrp3_div2_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp3_div2_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x101, 0x0) + +# clkgrp3_div2_cfg5_drvr_res[1:0] = 0x3 +# clkgrp3_div2_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp3_div2_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp3_div2_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp3_div2_cfg2_mutesel[7:6] = 0x0 +dut.write(0x102, 0xB) + +# clkgrp4_div1_cfg1_en[0:0] = 0x1 +# clkgrp4_div1_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp4_div1_cfg2_startmode[3:2] = 0x0 +# clkgrp4_div1_cfg1_rev[4:4] = 0x1 +# clkgrp4_div1_cfg1_slipmask[5:5] = 0x1 +# clkgrp4_div1_cfg1_reseedmask[6:6] = 0x1 +# clkgrp4_div1_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x104, 0x73) + +# clkgrp4_div1_cfg12_divrat_lsb[7:0] = 0x4 +dut.write(0x105, 0x4) + +# clkgrp4_div1_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x106, 0x0) + +# clkgrp4_div1_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x107, 0x0) + +# clkgrp4_div1_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x108, 0x0) + +# clkgrp4_div1_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x109, 0x0) + +# clkgrp4_div1_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x10A, 0x0) + +# clkgrp4_div1_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp4_div1_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x10B, 0x0) + +# clkgrp4_div1_cfg5_drvr_res[1:0] = 0x0 +# clkgrp4_div1_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp4_div1_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp4_div1_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp4_div1_cfg2_mutesel[7:6] = 0x0 +dut.write(0x10C, 0x8) + +# clkgrp4_div2_cfg1_en[0:0] = 0x1 +# clkgrp4_div2_cfg1_phdelta_mslip[1:1] = 0x0 +# clkgrp4_div2_cfg2_startmode[3:2] = 0x0 +# clkgrp4_div2_cfg1_rev[4:4] = 0x1 +# clkgrp4_div2_cfg1_slipmask[5:5] = 0x1 +# clkgrp4_div2_cfg1_reseedmask[6:6] = 0x1 +# clkgrp4_div2_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x10E, 0x71) + +# clkgrp4_div2_cfg12_divrat_lsb[7:0] = 0x40 +dut.write(0x10F, 0x40) + +# clkgrp4_div2_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x110, 0x0) + +# clkgrp4_div2_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x111, 0x0) + +# clkgrp4_div2_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x112, 0x0) + +# clkgrp4_div2_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x113, 0x0) + +# clkgrp4_div2_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x114, 0x0) + +# clkgrp4_div2_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp4_div2_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x115, 0x0) + +# clkgrp4_div2_cfg5_drvr_res[1:0] = 0x3 +# clkgrp4_div2_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp4_div2_cfg5_drvr_mode[4:3] = 0x2 +# clkgrp4_div2_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp4_div2_cfg2_mutesel[7:6] = 0x0 +dut.write(0x116, 0x13) + +# clkgrp5_div1_cfg1_en[0:0] = 0x1 +# clkgrp5_div1_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp5_div1_cfg2_startmode[3:2] = 0x0 +# clkgrp5_div1_cfg1_rev[4:4] = 0x1 +# clkgrp5_div1_cfg1_slipmask[5:5] = 0x1 +# clkgrp5_div1_cfg1_reseedmask[6:6] = 0x1 +# clkgrp5_div1_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x118, 0x73) + +# clkgrp5_div1_cfg12_divrat_lsb[7:0] = 0x4 +dut.write(0x119, 0x4) + +# clkgrp5_div1_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x11A, 0x0) + +# clkgrp5_div1_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x11B, 0x0) + +# clkgrp5_div1_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x11C, 0x0) + +# clkgrp5_div1_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x11D, 0x0) + +# clkgrp5_div1_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x11E, 0x0) + +# clkgrp5_div1_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp5_div1_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x11F, 0x0) + +# clkgrp5_div1_cfg5_drvr_res[1:0] = 0x0 +# clkgrp5_div1_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp5_div1_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp5_div1_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp5_div1_cfg2_mutesel[7:6] = 0x0 +dut.write(0x120, 0x8) + +# clkgrp5_div2_cfg1_en[0:0] = 0x1 +# clkgrp5_div2_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp5_div2_cfg2_startmode[3:2] = 0x0 +# clkgrp5_div2_cfg1_rev[4:4] = 0x1 +# clkgrp5_div2_cfg1_slipmask[5:5] = 0x1 +# clkgrp5_div2_cfg1_reseedmask[6:6] = 0x1 +# clkgrp5_div2_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x122, 0x73) + +# clkgrp5_div2_cfg12_divrat_lsb[7:0] = 0x4 +dut.write(0x123, 0x4) + +# clkgrp5_div2_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x124, 0x0) + +# clkgrp5_div2_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x125, 0x0) + +# clkgrp5_div2_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x126, 0x0) + +# clkgrp5_div2_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x127, 0x0) + +# clkgrp5_div2_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x128, 0x0) + +# clkgrp5_div2_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp5_div2_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x129, 0x0) + +# clkgrp5_div2_cfg5_drvr_res[1:0] = 0x3 +# clkgrp5_div2_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp5_div2_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp5_div2_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp5_div2_cfg2_mutesel[7:6] = 0x0 +dut.write(0x12A, 0xB) + +# clkgrp6_div1_cfg1_en[0:0] = 0x1 +# clkgrp6_div1_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp6_div1_cfg2_startmode[3:2] = 0x0 +# clkgrp6_div1_cfg1_rev[4:4] = 0x1 +# clkgrp6_div1_cfg1_slipmask[5:5] = 0x1 +# clkgrp6_div1_cfg1_reseedmask[6:6] = 0x1 +# clkgrp6_div1_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x12C, 0x73) + +# clkgrp6_div1_cfg12_divrat_lsb[7:0] = 0x4 +dut.write(0x12D, 0x4) + +# clkgrp6_div1_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x12E, 0x0) + +# clkgrp6_div1_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x12F, 0x0) + +# clkgrp6_div1_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x130, 0x0) + +# clkgrp6_div1_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x131, 0x0) + +# clkgrp6_div1_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x132, 0x0) + +# clkgrp6_div1_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp6_div1_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x133, 0x0) + +# clkgrp6_div1_cfg5_drvr_res[1:0] = 0x0 +# clkgrp6_div1_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp6_div1_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp6_div1_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp6_div1_cfg2_mutesel[7:6] = 0x0 +dut.write(0x134, 0x8) + +# clkgrp6_div2_cfg1_en[0:0] = 0x1 +# clkgrp6_div2_cfg1_phdelta_mslip[1:1] = 0x0 +# clkgrp6_div2_cfg2_startmode[3:2] = 0x0 +# clkgrp6_div2_cfg1_rev[4:4] = 0x1 +# clkgrp6_div2_cfg1_slipmask[5:5] = 0x1 +# clkgrp6_div2_cfg1_reseedmask[6:6] = 0x1 +# clkgrp6_div2_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x136, 0x71) + +# clkgrp6_div2_cfg12_divrat_lsb[7:0] = 0x80 +dut.write(0x137, 0x80) + +# clkgrp6_div2_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x138, 0x0) + +# clkgrp6_div2_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x139, 0x0) + +# clkgrp6_div2_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x13A, 0x0) + +# clkgrp6_div2_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x13B, 0x0) + +# clkgrp6_div2_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x13C, 0x0) + +# clkgrp6_div2_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp6_div2_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x13D, 0x0) + +# clkgrp6_div2_cfg5_drvr_res[1:0] = 0x1 +# clkgrp6_div2_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp6_div2_cfg5_drvr_mode[4:3] = 0x2 +# clkgrp6_div2_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp6_div2_cfg2_mutesel[7:6] = 0x0 +dut.write(0x13E, 0x11) + +# clkgrp7_div1_cfg1_en[0:0] = 0x0 +# clkgrp7_div1_cfg1_phdelta_mslip[1:1] = 0x1 +# clkgrp7_div1_cfg2_startmode[3:2] = 0x0 +# clkgrp7_div1_cfg1_rev[4:4] = 0x1 +# clkgrp7_div1_cfg1_slipmask[5:5] = 0x1 +# clkgrp7_div1_cfg1_reseedmask[6:6] = 0x1 +# clkgrp7_div1_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x140, 0x72) + +# clkgrp7_div1_cfg12_divrat_lsb[7:0] = 0x2 +dut.write(0x141, 0x2) + +# clkgrp7_div1_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x142, 0x0) + +# clkgrp7_div1_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x143, 0x0) + +# clkgrp7_div1_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x144, 0x0) + +# clkgrp7_div1_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x145, 0x0) + +# clkgrp7_div1_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x146, 0x0) + +# clkgrp7_div1_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp7_div1_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x147, 0x0) + +# clkgrp7_div1_cfg5_drvr_res[1:0] = 0x0 +# clkgrp7_div1_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp7_div1_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp7_div1_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp7_div1_cfg2_mutesel[7:6] = 0x0 +dut.write(0x148, 0x8) + +# clkgrp7_div2_cfg1_en[0:0] = 0x0 +# clkgrp7_div2_cfg1_phdelta_mslip[1:1] = 0x0 +# clkgrp7_div2_cfg2_startmode[3:2] = 0x0 +# clkgrp7_div2_cfg1_rev[4:4] = 0x1 +# clkgrp7_div2_cfg1_slipmask[5:5] = 0x1 +# clkgrp7_div2_cfg1_reseedmask[6:6] = 0x1 +# clkgrp7_div2_cfg1_hi_perf[7:7] = 0x0 +dut.write(0x14A, 0x70) + +# clkgrp7_div2_cfg12_divrat_lsb[7:0] = 0x80 +dut.write(0x14B, 0x80) + +# clkgrp7_div2_cfg12_divrat_msb[3:0] = 0x0 +dut.write(0x14C, 0x0) + +# clkgrp7_div2_cfg5_fine_delay[4:0] = 0x0 +dut.write(0x14D, 0x0) + +# clkgrp7_div2_cfg5_sel_coarse_delay[4:0] = 0x0 +dut.write(0x14E, 0x0) + +# clkgrp7_div2_cfg12_mslip_lsb[7:0] = 0x0 +dut.write(0x14F, 0x0) + +# clkgrp7_div2_cfg12_mslip_msb[3:0] = 0x0 +dut.write(0x150, 0x0) + +# clkgrp7_div2_cfg2_sel_outmux[1:0] = 0x0 +# clkgrp7_div2_cfg1_drvr_sel_testclk[2:2] = 0x0 +dut.write(0x151, 0x0) + +# clkgrp7_div2_cfg5_drvr_res[1:0] = 0x3 +# clkgrp7_div2_cfg5_drvr_spare[2:2] = 0x0 +# clkgrp7_div2_cfg5_drvr_mode[4:3] = 0x1 +# clkgrp7_div2_cfg_outbuf_dyn[5:5] = 0x0 +# clkgrp7_div2_cfg2_mutesel[7:6] = 0x0 +dut.write(0x152, 0xB) + diff --git a/artiq/firmware/libboard/hmc830_7043.rs b/artiq/firmware/libboard/hmc830_7043.rs index 9d2c20b4a..21d190997 100644 --- a/artiq/firmware/libboard/hmc830_7043.rs +++ b/artiq/firmware/libboard/hmc830_7043.rs @@ -1,3 +1,5 @@ +include!(concat!(env!("OUT_DIR"), "/hmc7043_writes.rs")); + pub fn init() -> Result<(), &'static str> { error!("HMC830/7043 support is not implemented"); Ok(()) From 33f053cff8f3ef9f1f27c890071a95924fad9742 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 6 Sep 2017 10:46:02 +0800 Subject: [PATCH 42/45] libboard: complete but undebugged support for HMC830/7043 programming --- artiq/firmware/libboard/hmc830_7043.rs | 151 ++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 3 deletions(-) diff --git a/artiq/firmware/libboard/hmc830_7043.rs b/artiq/firmware/libboard/hmc830_7043.rs index 21d190997..3b6a2fae6 100644 --- a/artiq/firmware/libboard/hmc830_7043.rs +++ b/artiq/firmware/libboard/hmc830_7043.rs @@ -1,6 +1,151 @@ -include!(concat!(env!("OUT_DIR"), "/hmc7043_writes.rs")); +/* + * HMC830 config: + * 100MHz input, 1GHz output + * fvco = (refclk / r_divider) * n_divider + * fout = fvco/2 + * + * HMC7043 config: + * dac clock: 1GHz (div=1) + * fpga clock: 250MHz (div=4) + * sysref clock: 15.625MHz (div=64) + */ + +mod hmc830 { + use csr; + + const HMC830_WRITES: [(u8, u32); 16] = [ + (0x0, 0x20), + (0x1, 0x2), + (0x2, 0x2), // r_divider + (0x5, 0x1628), + (0x5, 0x60a0), + (0x5, 0xe110), + (0x5, 0x2818), + (0x5, 0x0), + (0x6, 0x303ca), + (0x7, 0x14d), + (0x8, 0xc1beff), + (0x9, 0x153fff), + (0xa, 0x2046), + (0xb, 0x7c061), + (0xf, 0x81), + (0x3, 0x28), // n_divider + ]; + + fn spi_setup() { + unsafe { + csr::converter_spi::offline_write(1); + csr::converter_spi::cs_polarity_write(0); + csr::converter_spi::clk_polarity_write(0); + csr::converter_spi::clk_phase_write(0); + csr::converter_spi::lsb_first_write(0); + csr::converter_spi::half_duplex_write(0); + csr::converter_spi::clk_div_write_write(8); + csr::converter_spi::clk_div_read_write(8); + csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC830_CS); + csr::converter_spi::offline_write(0); + } + } + + fn write(addr: u8, data: u32) { + let cmd = (0 << 6) | addr; + let val = ((cmd as u32) << 24) | data; + unsafe { + csr::converter_spi::xfer_len_write_write(32); + csr::converter_spi::xfer_len_read_write(0); + csr::converter_spi::data_write_write(val << (32-31)); + while csr::converter_spi::pending_read() != 0 {} + while csr::converter_spi::active_read() != 0 {} + } + } + + fn read(addr: u8) -> u32 { + let cmd = (1 << 6) | addr; + let val = (cmd as u32) << 24; + unsafe { + csr::converter_spi::xfer_len_write_write(7); + csr::converter_spi::xfer_len_read_write(25); + csr::converter_spi::data_write_write(val << (32-31)); + while csr::converter_spi::pending_read() != 0 {} + while csr::converter_spi::active_read() != 0 {} + csr::converter_spi::data_read_read() + } + } + + pub fn init() -> Result<(), &'static str> { + spi_setup(); + let id = read(0); + if id != 0xa7975 { + error!("invalid HMC830 ID: 0x{:08x}", id); + return Err("invalid HMC830 identification"); + } + for &(addr, data) in HMC830_WRITES.iter() { + write(addr, data); + } + Ok(()) + } +} + +mod hmc7043 { + use csr; + + include!(concat!(env!("OUT_DIR"), "/hmc7043_writes.rs")); + + fn spi_setup() { + unsafe { + csr::converter_spi::offline_write(1); + csr::converter_spi::cs_polarity_write(0); + csr::converter_spi::clk_polarity_write(0); + csr::converter_spi::clk_phase_write(0); + csr::converter_spi::lsb_first_write(0); + csr::converter_spi::half_duplex_write(1); + csr::converter_spi::clk_div_write_write(8); + csr::converter_spi::clk_div_read_write(8); + csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC7043_CS); + csr::converter_spi::offline_write(0); + } + } + + fn write(addr: u16, data: u8) { + let cmd = (0 << 15) | addr; + let val = ((cmd as u32) << 8) | data as u32; + unsafe { + csr::converter_spi::xfer_len_write_write(24); + csr::converter_spi::xfer_len_read_write(0); + csr::converter_spi::data_write_write(val << (32-24)); + while csr::converter_spi::pending_read() != 0 {} + while csr::converter_spi::active_read() != 0 {} + } + } + + fn read(addr: u16) -> u8 { + let cmd = (0 << 15) | addr; + let val = (cmd as u32) << 8; + unsafe { + csr::converter_spi::xfer_len_write_write(16); + csr::converter_spi::xfer_len_read_write(8); + csr::converter_spi::data_write_write(val << (32-24)); + while csr::converter_spi::pending_read() != 0 {} + while csr::converter_spi::active_read() != 0 {} + csr::converter_spi::data_read_read() as u8 + } + } + + pub fn init() -> Result<(), &'static str> { + spi_setup(); + let id = (read(0x78) as u32) << 16 | (read(0x79) as u32) << 8 | read(0x7a) as u32; + if id != 0xf17904 { + error!("invalid HMC7043 ID: 0x{:08x}", id); + return Err("invalid HMC7043 identification"); + } + for &(addr, data) in HMC7043_WRITES.iter() { + write(addr, data); + } + Ok(()) + } +} pub fn init() -> Result<(), &'static str> { - error!("HMC830/7043 support is not implemented"); - Ok(()) + hmc830::init()?; + hmc7043::init() } From 2b2b345eb9d2e60b5df93efd1ccee9dc032febf2 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 6 Sep 2017 11:07:07 +0800 Subject: [PATCH 43/45] firmware: wait for serwb to be ready before proceeding further --- artiq/firmware/libboard/lib.rs | 2 ++ artiq/firmware/libboard/serwb.rs | 18 ++++++++++++++++++ artiq/firmware/runtime/lib.rs | 3 +++ artiq/firmware/satman/lib.rs | 3 +++ 4 files changed, 26 insertions(+) create mode 100644 artiq/firmware/libboard/serwb.rs diff --git a/artiq/firmware/libboard/lib.rs b/artiq/firmware/libboard/lib.rs index edeef1e69..738740273 100644 --- a/artiq/firmware/libboard/lib.rs +++ b/artiq/firmware/libboard/lib.rs @@ -25,6 +25,8 @@ pub mod spi; #[cfg(has_si5324)] pub mod si5324; +#[cfg(has_serwb_phy)] +pub mod serwb; #[cfg(has_ad9516)] #[allow(dead_code)] mod ad9516_reg; diff --git a/artiq/firmware/libboard/serwb.rs b/artiq/firmware/libboard/serwb.rs new file mode 100644 index 000000000..ce7fbf2cf --- /dev/null +++ b/artiq/firmware/libboard/serwb.rs @@ -0,0 +1,18 @@ +use csr; + +pub fn wait_init() { + info!("waiting for AMC/RTM serwb bridge to be ready..."); + unsafe { + while csr::serwb_phy::control_ready_read() != 0 {} + } + info!("done."); + + // Try reading the identifier register on the other side of the bridge. + let rtm_identifier = unsafe { + csr::rtm_identifier::identifier_read() + }; + if rtm_identifier != 0x5352544d { + error!("incorrect RTM identifier: 0x{:08x}", rtm_identifier); + // proceed anyway + } +} diff --git a/artiq/firmware/runtime/lib.rs b/artiq/firmware/runtime/lib.rs index 80b780497..e09cf321d 100644 --- a/artiq/firmware/runtime/lib.rs +++ b/artiq/firmware/runtime/lib.rs @@ -63,6 +63,9 @@ fn startup() { info!("software version {}", include_str!(concat!(env!("OUT_DIR"), "/git-describe"))); info!("gateware version {}", board::ident(&mut [0; 64])); + #[cfg(has_serwb_phy)] + board::serwb::wait_init(); + let t = board::clock::get_ms(); info!("press 'e' to erase startup and idle kernels..."); while board::clock::get_ms() < t + 1000 { diff --git a/artiq/firmware/satman/lib.rs b/artiq/firmware/satman/lib.rs index 3137e7e04..95fbb6e59 100644 --- a/artiq/firmware/satman/lib.rs +++ b/artiq/firmware/satman/lib.rs @@ -195,6 +195,9 @@ fn startup() { info!("software version {}", include_str!(concat!(env!("OUT_DIR"), "/git-describe"))); info!("gateware version {}", board::ident(&mut [0; 64])); + #[cfg(has_serwb_phy)] + board::serwb::wait_init(); + #[cfg(has_ad9516)] board::ad9516::init().expect("cannot initialize AD9516"); #[cfg(has_hmc830_7043)] From 34ec37ac85338a233fb8848a36335bc2d1539a0c Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 6 Sep 2017 11:09:38 +0800 Subject: [PATCH 44/45] conda: bump misoc --- conda/artiq-dev/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda/artiq-dev/meta.yaml b/conda/artiq-dev/meta.yaml index 3baaaaaf7..ada52d366 100644 --- a/conda/artiq-dev/meta.yaml +++ b/conda/artiq-dev/meta.yaml @@ -15,7 +15,7 @@ requirements: - python >=3.5.3,<3.6 - setuptools 33.1.1 - migen 0.5.dev py_143+git2e53295 - - misoc 0.6.dev py_62+git7d59ec36 + - misoc 0.6.dev py_64+git7d9e6057 - jesd204b 0.3 - binutils-or1k-linux >=2.27 - llvm-or1k From 2091c7696a24f5468a81ce4d6b74841d89fbcba0 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Wed, 6 Sep 2017 09:18:12 +0200 Subject: [PATCH 45/45] artiq/gateware/targets/sayma_amc_standalone: fix serwb_pll vco_div and serwb_phy mode --- artiq/gateware/targets/sayma_amc_standalone.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/gateware/targets/sayma_amc_standalone.py b/artiq/gateware/targets/sayma_amc_standalone.py index 026fc1159..4100541dc 100755 --- a/artiq/gateware/targets/sayma_amc_standalone.py +++ b/artiq/gateware/targets/sayma_amc_standalone.py @@ -57,12 +57,12 @@ class SaymaAMCStandalone(MiniSoC, AMPSoC): ] # AMC/RTM serwb - serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=1) + serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=2) self.comb += serwb_pll.refclk.eq(self.crg.cd_sys.clk) self.submodules += serwb_pll serwb_pads = platform.request("amc_rtm_serwb") - serwb_phy = serwb.phy.SERWBPHY(platform.device, serwb_pll, serwb_pads, mode="slave") + serwb_phy = serwb.phy.SERWBPHY(platform.device, serwb_pll, serwb_pads, mode="master") self.submodules.serwb_phy = serwb_phy self.csr_devices.append("serwb_phy")