From 901073acf303c0f03cd400c7fa8b84226e296b9b Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 30 Nov 2014 00:13:54 +0800 Subject: [PATCH] asynchronous RTIO --- artiq/coredevice/rtio.py | 8 +- artiq/coredevice/runtime.py | 3 +- soc/artiqlib/rtio/core.py | 275 ++++++++++++++++++++++++++---------- soc/artiqlib/rtio/phy.py | 10 +- soc/artiqlib/rtio/rbus.py | 8 +- soc/runtime/rtio.c | 30 ++-- soc/runtime/rtio.h | 3 +- soc/runtime/services.c | 1 - soc/targets/artiq.py | 5 +- 9 files changed, 226 insertions(+), 117 deletions(-) diff --git a/artiq/coredevice/rtio.py b/artiq/coredevice/rtio.py index 354a11054..d8dffb511 100644 --- a/artiq/coredevice/rtio.py +++ b/artiq/coredevice/rtio.py @@ -107,7 +107,8 @@ class RTIOOut(_RTIOBase): the output of the DDS). """ - syscall("rtio_sync", self.channel) + while syscall("rtio_get_counter") < self.previous_timestamp: + pass @kernel def on(self): @@ -192,7 +193,7 @@ class RTIOIn(_RTIOBase): """ count = 0 - while syscall("rtio_get", self.channel) >= 0: + while syscall("rtio_get", self.channel, self.previous_timestamp) >= 0: count += 1 return count @@ -204,4 +205,5 @@ class RTIOIn(_RTIOBase): If the gate is permanently closed, returns a negative value. """ - return cycles_to_time(syscall("rtio_get", self.channel)) + return cycles_to_time(syscall("rtio_get", self.channel, + self.previous_timestamp)) diff --git a/artiq/coredevice/runtime.py b/artiq/coredevice/runtime.py index d981828b2..4e377d796 100644 --- a/artiq/coredevice/runtime.py +++ b/artiq/coredevice/runtime.py @@ -16,9 +16,8 @@ _syscalls = { "rtio_oe": "ib:n", "rtio_set": "Iii:n", "rtio_replace": "Iii:n", - "rtio_sync": "i:n", "rtio_get_counter": "n:I", - "rtio_get": "i:I", + "rtio_get": "iI:I", "rtio_pileup_count": "i:i", "dds_phase_clear_en": "ib:n", "dds_program": "IiiiIbb:n", diff --git a/soc/artiqlib/rtio/core.py b/soc/artiqlib/rtio/core.py index 1d7086851..88fc4ef15 100644 --- a/soc/artiqlib/rtio/core.py +++ b/soc/artiqlib/rtio/core.py @@ -2,52 +2,156 @@ from fractions import Fraction from migen.fhdl.std import * from migen.bank.description import * -from migen.genlib.fifo import SyncFIFO +from migen.genlib.cdc import * +from migen.genlib.fifo import AsyncFIFO +from migen.genlib.resetsync import AsyncResetSynchronizer from artiqlib.rtio.rbus import get_fine_ts_width -class _RTIOBankO(Module): - def __init__(self, rbus, counter, fine_ts_width, fifo_depth): - counter_width = flen(counter) - self.sel = Signal(max=len(rbus)) - self.timestamp = Signal(counter_width+fine_ts_width) - self.value = Signal(2) - self.writable = Signal() - self.we = Signal() - self.replace = Signal() - self.underflow = Signal() - self.level = Signal(bits_for(fifo_depth)) +class _GrayCodeTransfer(Module): + def __init__(self, width): + self.i = Signal(width) # in rio domain + self.o = Signal(width) # in rsys domain # # # - # detect underflows - self.sync += \ - If((self.we & self.writable) | self.replace, - If(self.timestamp[fine_ts_width:] < counter + 2, - self.underflow.eq(1)) - ) + # convert to Gray code + value_gray_rio = Signal(width) + self.sync.rio += value_gray_rio.eq(self.i ^ self.i[1:]) + # transfer to system clock domain + value_gray_sys = Signal(width) + self.specials += [ + NoRetiming(value_gray_rio), + MultiReg(value_gray_rio, value_gray_sys, "rsys") + ] + # convert back to binary + value_sys = Signal(width) + self.comb += value_sys[-1].eq(value_gray_sys[-1]) + for i in reversed(range(width-1)): + self.comb += value_sys[i].eq(value_sys[i+1] ^ value_gray_sys[i]) + self.sync.rsys += self.o.eq(value_sys) + +class _RTIOCounter(Module): + def __init__(self, width, loopback_latency): + self.width = width + # Timestamp counter in RTIO domain for outputs + self.o_value_rio = Signal(width) + # Timestamp counter resynchronized to sys domain + # Lags behind o_value_rio, monotonic and glitch-free + self.o_value_sys = Signal(width) + # Timestamp counter in RTIO domain for inputs, + # compensated for PHY loopback latency + self.i_value_rio = Signal(width, reset=2**width-loopback_latency) + + # # # + + self.sync.rio += [ + self.o_value_rio.eq(self.o_value_rio + 1), + self.i_value_rio.eq(self.i_value_rio + 1) + ] + gt = _GrayCodeTransfer(width) + self.submodules += gt + self.comb += gt.i.eq(self.o_value_rio), self.o_value_sys.eq(gt.o) + + +# CHOOSING A GUARD TIME +# +# The buffer must be transferred to the FIFO soon enough to account for: +# * transfer of counter to sys domain: Tio + 2*Tsys + Tsys +# * guard time detection latency: Tsys +# * FIFO latency: Tsys + 2*Tio +# Therefore we must choose: +# guard_io_cycles > (3*Tio + 5*Tsys)/Tio +# +# We are writing to the FIFO from the buffer when the guard time has been +# reached without checking the FIFO's writable status. If the FIFO is full, +# this will produce an overflow and the event will be incorrectly discarded. +# +# When the FIFO is full, it contains fifo_depth events of strictly increasing +# timestamps. +# +# Thus the overflow-causing event's timestamp must satisfy: +# timestamp*Tio > fifo_depth*Tio + time +# We also have (guard time reached): +# timestamp*Tio < time + guard_io_cycles*Tio +# [NB: time > counter.o_value_sys*Tio] +# Thus we must have: +# guard_io_cycles > fifo_depth +# +# We can prevent overflows by choosing instead: +# guard_io_cycles < fifo_depth + +class _RTIOBankO(Module): + def __init__(self, rbus, counter, fine_ts_width, fifo_depth, guard_io_cycles): + self.sel = Signal(max=len(rbus)) + self.timestamp = Signal(counter.width + fine_ts_width) + self.value = Signal(2) + self.writable = Signal() + self.we = Signal() # maximum throughput 1/2 + self.replace = Signal() + self.underflow = Signal() # valid 2 cycles after we/replace + self.underflow_reset = Signal() + + # # # + + signal_underflow = Signal() fifos = [] + fifo_layout = [("timestamp", counter.width + fine_ts_width), + ("value", 2)] for n, chif in enumerate(rbus): - fifo = SyncFIFO([ - ("timestamp", counter_width+fine_ts_width), ("value", 2)], - 2 if chif.mini else fifo_depth) + # FIFO + fifo = RenameClockDomains(AsyncFIFO(fifo_layout, fifo_depth), + {"write": "rsys", "read": "rio"}) self.submodules += fifo fifos.append(fifo) - # FIFO replace/write + # Buffer + buf_valid = Signal() + buf_timestamp = Signal(counter.width + fine_ts_width) + buf_value = Signal(2) + buf_just_written = Signal() + + # Buffer read and FIFO write self.comb += [ - fifo.din.timestamp.eq(self.timestamp), - fifo.din.value.eq(self.value), - fifo.we.eq((self.we | self.replace) & (self.sel == n)), - fifo.replace.eq(self.replace) + fifo.din.timestamp.eq(buf_timestamp), + fifo.din.value.eq(buf_value) + ] + in_guard_time = Signal() + self.comb += in_guard_time.eq( + buf_timestamp[fine_ts_width:] < counter.o_value_sys + guard_io_cycles) + self.sync.rsys += If(in_guard_time, buf_valid.eq(0)) + self.comb += \ + If(buf_valid, + If(in_guard_time, + If(buf_just_written, + signal_underflow.eq(1) + ).Else( + fifo.we.eq(1) + ) + ), + If(self.we & (self.sel == n), fifo.we.eq(1)) + ) + + # Buffer write + # Must come after read to handle concurrent read+write properly + self.sync.rsys += [ + buf_just_written.eq(0), + If((self.we | self.replace) & (self.sel == n), + # Replace operations on empty buffers may happen + # on underflows, which will be correctly reported. + buf_just_written.eq(1), + buf_valid.eq(1), + buf_timestamp.eq(self.timestamp), + buf_value.eq(self.value) + ) ] # FIFO read self.comb += [ chif.o_stb.eq(fifo.readable & - (fifo.dout.timestamp[fine_ts_width:] == counter)), + (fifo.dout.timestamp[fine_ts_width:] == counter.o_value_rio)), chif.o_value.eq(fifo.dout.value), fifo.re.eq(chif.o_stb) ] @@ -55,22 +159,23 @@ class _RTIOBankO(Module): self.comb += chif.o_fine_ts.eq( fifo.dout.timestamp[:fine_ts_width]) - selfifo = Array(fifos)[self.sel] - self.comb += [ - self.writable.eq(selfifo.writable), - self.level.eq(selfifo.level) + self.comb += \ + self.writable.eq(Array(fifo.writable for fifo in fifos)[self.sel]) + self.sync.rsys += [ + If(self.underflow_reset, self.underflow.eq(0)), + If(signal_underflow, self.underflow.eq(1)) ] class _RTIOBankI(Module): def __init__(self, rbus, counter, fine_ts_width, fifo_depth): - counter_width = flen(counter) self.sel = Signal(max=len(rbus)) - self.timestamp = Signal(counter_width+fine_ts_width) + self.timestamp = Signal(counter.width + fine_ts_width) self.value = Signal() self.readable = Signal() self.re = Signal() self.overflow = Signal() + self.overflow_reset = Signal() self.pileup_count = Signal(16) self.pileup_reset = Signal() @@ -81,22 +186,23 @@ class _RTIOBankI(Module): readables = [] overflows = [] pileup_counts = [] + fifo_layout = [("timestamp", counter.width+fine_ts_width), + ("value", 1)] for n, chif in enumerate(rbus): if hasattr(chif, "oe"): sensitivity = Signal(2) - self.sync += If(~chif.oe & chif.o_stb, - sensitivity.eq(chif.o_value)) + self.sync.rio += If(~chif.oe & chif.o_stb, + sensitivity.eq(chif.o_value)) - fifo = SyncFIFO([ - ("timestamp", counter_width+fine_ts_width), ("value", 1)], - fifo_depth) + fifo = RenameClockDomains(AsyncFIFO(fifo_layout, fifo_depth), + {"read": "rsys", "write": "rio"}) self.submodules += fifo # FIFO write if fine_ts_width: - full_ts = Cat(chif.i_fine_ts, counter) + full_ts = Cat(chif.i_fine_ts, counter.i_value_rio) else: - full_ts = counter + full_ts = counter.i_value_rio self.comb += [ fifo.din.timestamp.eq(full_ts), fifo.din.value.eq(chif.i_value), @@ -113,19 +219,35 @@ class _RTIOBankI(Module): self.comb += fifo.re.eq(self.re & (self.sel == n)) overflow = Signal() - self.sync += If(fifo.we & ~fifo.writable, overflow.eq(1)) - overflows.append(overflow) + overflow_reset_sync = PulseSynchronizer("rsys", "rio") + self.submodules += overflow_reset_sync + self.comb += overflow_reset_sync.i.eq( + self.overflow_reset & (self.sel == n)) + self.sync.rio += [ + If(overflow_reset_sync.o, overflow.eq(0)), + If(fifo.we & ~fifo.writable, overflow.eq(1)) + ] + overflow_sys = Signal() + self.specials += MultiReg(overflow, overflow_sys, "rsys") + overflows.append(overflow_sys) pileup_count = Signal(16) - self.sync += \ - If(self.pileup_reset & (self.sel == n), + pileup_count_reset_sync = PulseSynchronizer("rsys", "rio") + self.submodules += pileup_count_reset_sync + self.comb += pileup_count_reset_sync.i.eq( + self.pileup_reset & (self.sel == n)) + self.sync.rio += \ + If(pileup_count_reset_sync.o, pileup_count.eq(0) ).Elif(chif.i_pileup, If(pileup_count != 2**16 - 1, # saturate pileup_count.eq(pileup_count + 1) ) ) - pileup_counts.append(pileup_count) + pileup_count_sync = _GrayCodeTransfer(16) + self.submodules += pileup_count_sync + self.comb += pileup_count_sync.i.eq(pileup_count) + pileup_counts.append(pileup_count_sync.o) else: timestamps.append(0) values.append(0) @@ -143,60 +265,63 @@ class _RTIOBankI(Module): class RTIO(Module, AutoCSR): - def __init__(self, phy, clk_freq, counter_width=32, ofifo_depth=64, ififo_depth=64): + def __init__(self, phy, clk_freq, counter_width=32, + ofifo_depth=64, ififo_depth=64, + guard_io_cycles=20): fine_ts_width = get_fine_ts_width(phy.rbus) - # Counters - reset_counter = Signal() - o_counter = Signal(counter_width, reset=phy.loopback_latency) - i_counter = Signal(counter_width) - self.sync += \ - If(reset_counter, - o_counter.eq(o_counter.reset), - i_counter.eq(i_counter.reset) - ).Else( - o_counter.eq(o_counter + 1), - i_counter.eq(i_counter + 1) - ) - # Submodules - self.submodules.bank_o = InsertReset(_RTIOBankO( - phy.rbus, - o_counter, fine_ts_width, ofifo_depth)) - self.submodules.bank_i = InsertReset(_RTIOBankI( - phy.rbus, - i_counter, fine_ts_width, ofifo_depth)) + self.submodules.counter = _RTIOCounter( + counter_width, phy.loopback_latency) + self.submodules.bank_o = _RTIOBankO( + phy.rbus, self.counter, fine_ts_width, ofifo_depth, guard_io_cycles) + self.submodules.bank_i = _RTIOBankI( + phy.rbus, self.counter, fine_ts_width, ofifo_depth) # CSRs - self._r_reset_logic = CSRStorage(reset=1) - self._r_reset_counter = CSRStorage(reset=1) + self._r_reset = CSRStorage(reset=1) self._r_chan_sel = CSRStorage(flen(self.bank_o.sel)) self._r_oe = CSR() - self._r_o_timestamp = CSRStorage(counter_width+fine_ts_width) + self._r_o_timestamp = CSRStorage(counter_width + fine_ts_width) self._r_o_value = CSRStorage(2) self._r_o_writable = CSRStatus() self._r_o_we = CSR() self._r_o_replace = CSR() self._r_o_underflow = CSRStatus() - self._r_o_level = CSRStatus(bits_for(ofifo_depth)) + self._r_o_underflow_reset = CSR() - self._r_i_timestamp = CSRStatus(counter_width+fine_ts_width) + self._r_i_timestamp = CSRStatus(counter_width + fine_ts_width) self._r_i_value = CSRStatus() self._r_i_readable = CSRStatus() self._r_i_re = CSR() self._r_i_overflow = CSRStatus() + self._r_i_overflow_reset = CSR() self._r_i_pileup_count = CSRStatus(16) self._r_i_pileup_reset = CSR() - self._r_counter = CSRStatus(counter_width+fine_ts_width) + self._r_counter = CSRStatus(counter_width + fine_ts_width) self._r_counter_update = CSR() self._r_frequency_i = CSRStatus(32) self._r_frequency_fn = CSRStatus(8) self._r_frequency_fd = CSRStatus(8) + + # Clocking/Reset + # Create rsys and rio domains based on sys and rio + # with reset controlled by CSR. + self.clock_domains.cd_rsys = ClockDomain() + self.clock_domains.cd_rio = ClockDomain() + self.comb += [ + self.cd_rsys.clk.eq(ClockSignal()), + self.cd_rsys.rst.eq(self._r_reset.storage) + ] + self.comb += self.cd_rio.clk.eq(ClockSignal("rtio")) + self.specials += AsyncResetSynchronizer( + self.cd_rio, self._r_reset.storage) + # OE oes = [] for n, chif in enumerate(phy.rbus): @@ -212,7 +337,6 @@ class RTIO(Module, AutoCSR): # Output/Gate self.comb += [ - self.bank_o.reset.eq(self._r_reset_logic.storage), self.bank_o.sel.eq(self._r_chan_sel.storage), self.bank_o.timestamp.eq(self._r_o_timestamp.storage), self.bank_o.value.eq(self._r_o_value.storage), @@ -220,28 +344,27 @@ class RTIO(Module, AutoCSR): self.bank_o.we.eq(self._r_o_we.re), self.bank_o.replace.eq(self._r_o_replace.re), self._r_o_underflow.status.eq(self.bank_o.underflow), - self._r_o_level.status.eq(self.bank_o.level) + self.bank_o.underflow_reset.eq(self._r_o_underflow_reset.re) ] # Input self.comb += [ - self.bank_i.reset.eq(self._r_reset_logic.storage), self.bank_i.sel.eq(self._r_chan_sel.storage), self._r_i_timestamp.status.eq(self.bank_i.timestamp), self._r_i_value.status.eq(self.bank_i.value), self._r_i_readable.status.eq(self.bank_i.readable), self.bank_i.re.eq(self._r_i_re.re), self._r_i_overflow.status.eq(self.bank_i.overflow), + self.bank_i.overflow_reset.eq(self._r_i_overflow_reset.re), self._r_i_pileup_count.status.eq(self.bank_i.pileup_count), self.bank_i.pileup_reset.eq(self._r_i_pileup_reset.re) ] # Counter access - self.comb += reset_counter.eq(self._r_reset_counter.storage) self.sync += \ If(self._r_counter_update.re, self._r_counter.status.eq(Cat(Replicate(0, fine_ts_width), - o_counter)) + self.counter.o_value_sys)) ) # Frequency diff --git a/soc/artiqlib/rtio/phy.py b/soc/artiqlib/rtio/phy.py index f1c34ccfa..9e5577db7 100644 --- a/soc/artiqlib/rtio/phy.py +++ b/soc/artiqlib/rtio/phy.py @@ -5,25 +5,25 @@ from artiqlib.rtio.rbus import create_rbus class SimplePHY(Module): - def __init__(self, pads, output_only_pads=set(), mini_pads=set()): - self.rbus = create_rbus(0, pads, output_only_pads, mini_pads) + def __init__(self, pads, output_only_pads=set()): + self.rbus = create_rbus(0, pads, output_only_pads) self.loopback_latency = 3 # # # for pad, chif in zip(pads, self.rbus): o_pad = Signal() - self.sync += If(chif.o_stb, o_pad.eq(chif.o_value)) + self.sync.rio += If(chif.o_stb, o_pad.eq(chif.o_value)) if hasattr(chif, "oe"): ts = TSTriple() i_pad = Signal() - self.sync += ts.oe.eq(chif.oe) + self.sync.rio += ts.oe.eq(chif.oe) self.comb += ts.o.eq(o_pad) self.specials += MultiReg(ts.i, i_pad), \ ts.get_tristate(pad) i_pad_d = Signal() - self.sync += i_pad_d.eq(i_pad) + self.sync.rio += i_pad_d.eq(i_pad) self.comb += chif.i_stb.eq(i_pad ^ i_pad_d), \ chif.i_value.eq(i_pad) else: diff --git a/soc/artiqlib/rtio/rbus.py b/soc/artiqlib/rtio/rbus.py index 9eb46f44c..821b3c2a6 100644 --- a/soc/artiqlib/rtio/rbus.py +++ b/soc/artiqlib/rtio/rbus.py @@ -2,7 +2,7 @@ from migen.fhdl.std import * from migen.genlib.record import Record -def create_rbus(fine_ts_bits, pads, output_only_pads, mini_pads): +def create_rbus(fine_ts_bits, pads, output_only_pads): rbus = [] for pad in pads: layout = [ @@ -11,7 +11,7 @@ def create_rbus(fine_ts_bits, pads, output_only_pads, mini_pads): ] if fine_ts_bits: layout.append(("o_fine_ts", fine_ts_bits)) - if pad not in output_only_pads and pad not in mini_pads: + if pad not in output_only_pads: layout += [ ("oe", 1), ("i_stb", 1), @@ -20,9 +20,7 @@ def create_rbus(fine_ts_bits, pads, output_only_pads, mini_pads): ] if fine_ts_bits: layout.append(("i_fine_ts", fine_ts_bits)) - chif = Record(layout) - chif.mini = pad in mini_pads - rbus.append(chif) + rbus.append(Record(layout)) return rbus diff --git a/soc/runtime/rtio.c b/soc/runtime/rtio.c index 27709f286..7e29e3f0e 100644 --- a/soc/runtime/rtio.c +++ b/soc/runtime/rtio.c @@ -8,10 +8,8 @@ long long int previous_fud_end_time; void rtio_init(void) { previous_fud_end_time = 0; - rtio_reset_counter_write(1); - rtio_reset_logic_write(1); - rtio_reset_counter_write(0); - rtio_reset_logic_write(0); + rtio_reset_write(1); + rtio_reset_write(0); } void rtio_oe(int channel, int oe) @@ -28,8 +26,7 @@ void rtio_set(long long int timestamp, int channel, int value) while(!rtio_o_writable_read()); rtio_o_we_write(1); if(rtio_o_underflow_read()) { - rtio_reset_logic_write(1); - rtio_reset_logic_write(0); + rtio_o_underflow_reset_write(1); exception_raise(EID_RTIO_UNDERFLOW); } } @@ -41,33 +38,25 @@ void rtio_replace(long long int timestamp, int channel, int value) rtio_o_value_write(value); rtio_o_replace_write(1); if(rtio_o_underflow_read()) { - rtio_reset_logic_write(1); - rtio_reset_logic_write(0); + rtio_o_underflow_reset_write(1); exception_raise(EID_RTIO_UNDERFLOW); } } -void rtio_sync(int channel) -{ - rtio_chan_sel_write(channel); - while(rtio_o_level_read() != 0); -} - long long int rtio_get_counter(void) { rtio_counter_update_write(1); return rtio_counter_read(); } -long long int rtio_get(int channel) +long long int rtio_get(int channel, long long int time_limit) { long long int r; rtio_chan_sel_write(channel); - while(rtio_i_readable_read() || (rtio_o_level_read() != 0)) { + while(rtio_i_readable_read() || (rtio_get_counter() < time_limit)) { if(rtio_i_overflow_read()) { - rtio_reset_logic_write(1); - rtio_reset_logic_write(0); + rtio_i_overflow_reset_write(1); exception_raise(EID_RTIO_OVERFLOW); } if(rtio_i_readable_read()) { @@ -93,7 +82,7 @@ int rtio_pileup_count(int channel) void rtio_fud_sync(void) { - rtio_sync(RTIO_FUD_CHANNEL); + while(rtio_get_counter() < previous_fud_end_time); } void rtio_fud(long long int fud_time) @@ -113,8 +102,7 @@ void rtio_fud(long long int fud_time) rtio_o_value_write(0); rtio_o_we_write(1); if(rtio_o_underflow_read()) { - rtio_reset_logic_write(1); - rtio_reset_logic_write(0); + rtio_o_underflow_reset_write(1); exception_raise(EID_RTIO_UNDERFLOW); } } diff --git a/soc/runtime/rtio.h b/soc/runtime/rtio.h index b26fdc3be..6ed35bb55 100644 --- a/soc/runtime/rtio.h +++ b/soc/runtime/rtio.h @@ -5,9 +5,8 @@ void rtio_init(void); void rtio_oe(int channel, int oe); void rtio_set(long long int timestamp, int channel, int value); void rtio_replace(long long int timestamp, int channel, int value); -void rtio_sync(int channel); long long int rtio_get_counter(void); -long long int rtio_get(int channel); +long long int rtio_get(int channel, long long int time_limit); int rtio_pileup_count(int channel); void rtio_fud_sync(void); diff --git a/soc/runtime/services.c b/soc/runtime/services.c index 1eb9f5b24..f07f8eadb 100644 --- a/soc/runtime/services.c +++ b/soc/runtime/services.c @@ -14,7 +14,6 @@ static const struct symbol syscalls[] = { {"rtio_oe", rtio_oe}, {"rtio_set", rtio_set}, {"rtio_replace", rtio_replace}, - {"rtio_sync", rtio_sync}, {"rtio_get_counter", rtio_get_counter}, {"rtio_get", rtio_get}, {"rtio_pileup_count", rtio_pileup_count}, diff --git a/soc/targets/artiq.py b/soc/targets/artiq.py index c381f6740..e2eb2e145 100644 --- a/soc/targets/artiq.py +++ b/soc/targets/artiq.py @@ -62,9 +62,10 @@ class ARTIQMiniSoC(BaseSoC): rtio_pads.append(fud) self.submodules.rtiophy = rtio.phy.SimplePHY( rtio_pads, - output_only_pads={rtio_pads[1], rtio_pads[2], rtio_pads[3]}, - mini_pads={fud}) + output_only_pads={rtio_pads[1], rtio_pads[2], rtio_pads[3], fud}) self.submodules.rtio = rtio.RTIO(self.rtiophy, self.clk_freq) + self.clock_domains.cd_rtio = ClockDomain() + self.comb += self.cd_rtio.clk.eq(ClockSignal()) if with_test_gen: self.submodules.test_gen = _TestGen(platform.request("ttl", 4))