mirror of https://github.com/m-labs/artiq.git
asynchronous RTIO
This commit is contained in:
parent
9c41f98d70
commit
901073acf3
|
@ -107,7 +107,8 @@ class RTIOOut(_RTIOBase):
|
||||||
the output of the DDS).
|
the output of the DDS).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
syscall("rtio_sync", self.channel)
|
while syscall("rtio_get_counter") < self.previous_timestamp:
|
||||||
|
pass
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def on(self):
|
def on(self):
|
||||||
|
@ -192,7 +193,7 @@ class RTIOIn(_RTIOBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
count = 0
|
count = 0
|
||||||
while syscall("rtio_get", self.channel) >= 0:
|
while syscall("rtio_get", self.channel, self.previous_timestamp) >= 0:
|
||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
@ -204,4 +205,5 @@ class RTIOIn(_RTIOBase):
|
||||||
If the gate is permanently closed, returns a negative value.
|
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))
|
||||||
|
|
|
@ -16,9 +16,8 @@ _syscalls = {
|
||||||
"rtio_oe": "ib:n",
|
"rtio_oe": "ib:n",
|
||||||
"rtio_set": "Iii:n",
|
"rtio_set": "Iii:n",
|
||||||
"rtio_replace": "Iii:n",
|
"rtio_replace": "Iii:n",
|
||||||
"rtio_sync": "i:n",
|
|
||||||
"rtio_get_counter": "n:I",
|
"rtio_get_counter": "n:I",
|
||||||
"rtio_get": "i:I",
|
"rtio_get": "iI:I",
|
||||||
"rtio_pileup_count": "i:i",
|
"rtio_pileup_count": "i:i",
|
||||||
"dds_phase_clear_en": "ib:n",
|
"dds_phase_clear_en": "ib:n",
|
||||||
"dds_program": "IiiiIbb:n",
|
"dds_program": "IiiiIbb:n",
|
||||||
|
|
|
@ -2,52 +2,156 @@ from fractions import Fraction
|
||||||
|
|
||||||
from migen.fhdl.std import *
|
from migen.fhdl.std import *
|
||||||
from migen.bank.description 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
|
from artiqlib.rtio.rbus import get_fine_ts_width
|
||||||
|
|
||||||
|
|
||||||
class _RTIOBankO(Module):
|
class _GrayCodeTransfer(Module):
|
||||||
def __init__(self, rbus, counter, fine_ts_width, fifo_depth):
|
def __init__(self, width):
|
||||||
counter_width = flen(counter)
|
self.i = Signal(width) # in rio domain
|
||||||
self.sel = Signal(max=len(rbus))
|
self.o = Signal(width) # in rsys domain
|
||||||
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))
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# detect underflows
|
# convert to Gray code
|
||||||
self.sync += \
|
value_gray_rio = Signal(width)
|
||||||
If((self.we & self.writable) | self.replace,
|
self.sync.rio += value_gray_rio.eq(self.i ^ self.i[1:])
|
||||||
If(self.timestamp[fine_ts_width:] < counter + 2,
|
# transfer to system clock domain
|
||||||
self.underflow.eq(1))
|
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 = []
|
fifos = []
|
||||||
|
fifo_layout = [("timestamp", counter.width + fine_ts_width),
|
||||||
|
("value", 2)]
|
||||||
for n, chif in enumerate(rbus):
|
for n, chif in enumerate(rbus):
|
||||||
fifo = SyncFIFO([
|
# FIFO
|
||||||
("timestamp", counter_width+fine_ts_width), ("value", 2)],
|
fifo = RenameClockDomains(AsyncFIFO(fifo_layout, fifo_depth),
|
||||||
2 if chif.mini else fifo_depth)
|
{"write": "rsys", "read": "rio"})
|
||||||
self.submodules += fifo
|
self.submodules += fifo
|
||||||
fifos.append(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 += [
|
self.comb += [
|
||||||
fifo.din.timestamp.eq(self.timestamp),
|
fifo.din.timestamp.eq(buf_timestamp),
|
||||||
fifo.din.value.eq(self.value),
|
fifo.din.value.eq(buf_value)
|
||||||
fifo.we.eq((self.we | self.replace) & (self.sel == n)),
|
]
|
||||||
fifo.replace.eq(self.replace)
|
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
|
# FIFO read
|
||||||
self.comb += [
|
self.comb += [
|
||||||
chif.o_stb.eq(fifo.readable &
|
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),
|
chif.o_value.eq(fifo.dout.value),
|
||||||
fifo.re.eq(chif.o_stb)
|
fifo.re.eq(chif.o_stb)
|
||||||
]
|
]
|
||||||
|
@ -55,22 +159,23 @@ class _RTIOBankO(Module):
|
||||||
self.comb += chif.o_fine_ts.eq(
|
self.comb += chif.o_fine_ts.eq(
|
||||||
fifo.dout.timestamp[:fine_ts_width])
|
fifo.dout.timestamp[:fine_ts_width])
|
||||||
|
|
||||||
selfifo = Array(fifos)[self.sel]
|
self.comb += \
|
||||||
self.comb += [
|
self.writable.eq(Array(fifo.writable for fifo in fifos)[self.sel])
|
||||||
self.writable.eq(selfifo.writable),
|
self.sync.rsys += [
|
||||||
self.level.eq(selfifo.level)
|
If(self.underflow_reset, self.underflow.eq(0)),
|
||||||
|
If(signal_underflow, self.underflow.eq(1))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class _RTIOBankI(Module):
|
class _RTIOBankI(Module):
|
||||||
def __init__(self, rbus, counter, fine_ts_width, fifo_depth):
|
def __init__(self, rbus, counter, fine_ts_width, fifo_depth):
|
||||||
counter_width = flen(counter)
|
|
||||||
self.sel = Signal(max=len(rbus))
|
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.value = Signal()
|
||||||
self.readable = Signal()
|
self.readable = Signal()
|
||||||
self.re = Signal()
|
self.re = Signal()
|
||||||
self.overflow = Signal()
|
self.overflow = Signal()
|
||||||
|
self.overflow_reset = Signal()
|
||||||
self.pileup_count = Signal(16)
|
self.pileup_count = Signal(16)
|
||||||
self.pileup_reset = Signal()
|
self.pileup_reset = Signal()
|
||||||
|
|
||||||
|
@ -81,22 +186,23 @@ class _RTIOBankI(Module):
|
||||||
readables = []
|
readables = []
|
||||||
overflows = []
|
overflows = []
|
||||||
pileup_counts = []
|
pileup_counts = []
|
||||||
|
fifo_layout = [("timestamp", counter.width+fine_ts_width),
|
||||||
|
("value", 1)]
|
||||||
for n, chif in enumerate(rbus):
|
for n, chif in enumerate(rbus):
|
||||||
if hasattr(chif, "oe"):
|
if hasattr(chif, "oe"):
|
||||||
sensitivity = Signal(2)
|
sensitivity = Signal(2)
|
||||||
self.sync += If(~chif.oe & chif.o_stb,
|
self.sync.rio += If(~chif.oe & chif.o_stb,
|
||||||
sensitivity.eq(chif.o_value))
|
sensitivity.eq(chif.o_value))
|
||||||
|
|
||||||
fifo = SyncFIFO([
|
fifo = RenameClockDomains(AsyncFIFO(fifo_layout, fifo_depth),
|
||||||
("timestamp", counter_width+fine_ts_width), ("value", 1)],
|
{"read": "rsys", "write": "rio"})
|
||||||
fifo_depth)
|
|
||||||
self.submodules += fifo
|
self.submodules += fifo
|
||||||
|
|
||||||
# FIFO write
|
# FIFO write
|
||||||
if fine_ts_width:
|
if fine_ts_width:
|
||||||
full_ts = Cat(chif.i_fine_ts, counter)
|
full_ts = Cat(chif.i_fine_ts, counter.i_value_rio)
|
||||||
else:
|
else:
|
||||||
full_ts = counter
|
full_ts = counter.i_value_rio
|
||||||
self.comb += [
|
self.comb += [
|
||||||
fifo.din.timestamp.eq(full_ts),
|
fifo.din.timestamp.eq(full_ts),
|
||||||
fifo.din.value.eq(chif.i_value),
|
fifo.din.value.eq(chif.i_value),
|
||||||
|
@ -113,19 +219,35 @@ class _RTIOBankI(Module):
|
||||||
self.comb += fifo.re.eq(self.re & (self.sel == n))
|
self.comb += fifo.re.eq(self.re & (self.sel == n))
|
||||||
|
|
||||||
overflow = Signal()
|
overflow = Signal()
|
||||||
self.sync += If(fifo.we & ~fifo.writable, overflow.eq(1))
|
overflow_reset_sync = PulseSynchronizer("rsys", "rio")
|
||||||
overflows.append(overflow)
|
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)
|
pileup_count = Signal(16)
|
||||||
self.sync += \
|
pileup_count_reset_sync = PulseSynchronizer("rsys", "rio")
|
||||||
If(self.pileup_reset & (self.sel == n),
|
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)
|
pileup_count.eq(0)
|
||||||
).Elif(chif.i_pileup,
|
).Elif(chif.i_pileup,
|
||||||
If(pileup_count != 2**16 - 1, # saturate
|
If(pileup_count != 2**16 - 1, # saturate
|
||||||
pileup_count.eq(pileup_count + 1)
|
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:
|
else:
|
||||||
timestamps.append(0)
|
timestamps.append(0)
|
||||||
values.append(0)
|
values.append(0)
|
||||||
|
@ -143,60 +265,63 @@ class _RTIOBankI(Module):
|
||||||
|
|
||||||
|
|
||||||
class RTIO(Module, AutoCSR):
|
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)
|
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
|
# Submodules
|
||||||
self.submodules.bank_o = InsertReset(_RTIOBankO(
|
self.submodules.counter = _RTIOCounter(
|
||||||
phy.rbus,
|
counter_width, phy.loopback_latency)
|
||||||
o_counter, fine_ts_width, ofifo_depth))
|
self.submodules.bank_o = _RTIOBankO(
|
||||||
self.submodules.bank_i = InsertReset(_RTIOBankI(
|
phy.rbus, self.counter, fine_ts_width, ofifo_depth, guard_io_cycles)
|
||||||
phy.rbus,
|
self.submodules.bank_i = _RTIOBankI(
|
||||||
i_counter, fine_ts_width, ofifo_depth))
|
phy.rbus, self.counter, fine_ts_width, ofifo_depth)
|
||||||
|
|
||||||
# CSRs
|
# CSRs
|
||||||
self._r_reset_logic = CSRStorage(reset=1)
|
self._r_reset = CSRStorage(reset=1)
|
||||||
self._r_reset_counter = CSRStorage(reset=1)
|
|
||||||
self._r_chan_sel = CSRStorage(flen(self.bank_o.sel))
|
self._r_chan_sel = CSRStorage(flen(self.bank_o.sel))
|
||||||
|
|
||||||
self._r_oe = CSR()
|
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_value = CSRStorage(2)
|
||||||
self._r_o_writable = CSRStatus()
|
self._r_o_writable = CSRStatus()
|
||||||
self._r_o_we = CSR()
|
self._r_o_we = CSR()
|
||||||
self._r_o_replace = CSR()
|
self._r_o_replace = CSR()
|
||||||
self._r_o_underflow = CSRStatus()
|
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_value = CSRStatus()
|
||||||
self._r_i_readable = CSRStatus()
|
self._r_i_readable = CSRStatus()
|
||||||
self._r_i_re = CSR()
|
self._r_i_re = CSR()
|
||||||
self._r_i_overflow = CSRStatus()
|
self._r_i_overflow = CSRStatus()
|
||||||
|
self._r_i_overflow_reset = CSR()
|
||||||
self._r_i_pileup_count = CSRStatus(16)
|
self._r_i_pileup_count = CSRStatus(16)
|
||||||
self._r_i_pileup_reset = CSR()
|
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_counter_update = CSR()
|
||||||
|
|
||||||
self._r_frequency_i = CSRStatus(32)
|
self._r_frequency_i = CSRStatus(32)
|
||||||
self._r_frequency_fn = CSRStatus(8)
|
self._r_frequency_fn = CSRStatus(8)
|
||||||
self._r_frequency_fd = 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
|
# OE
|
||||||
oes = []
|
oes = []
|
||||||
for n, chif in enumerate(phy.rbus):
|
for n, chif in enumerate(phy.rbus):
|
||||||
|
@ -212,7 +337,6 @@ class RTIO(Module, AutoCSR):
|
||||||
|
|
||||||
# Output/Gate
|
# Output/Gate
|
||||||
self.comb += [
|
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.sel.eq(self._r_chan_sel.storage),
|
||||||
self.bank_o.timestamp.eq(self._r_o_timestamp.storage),
|
self.bank_o.timestamp.eq(self._r_o_timestamp.storage),
|
||||||
self.bank_o.value.eq(self._r_o_value.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.we.eq(self._r_o_we.re),
|
||||||
self.bank_o.replace.eq(self._r_o_replace.re),
|
self.bank_o.replace.eq(self._r_o_replace.re),
|
||||||
self._r_o_underflow.status.eq(self.bank_o.underflow),
|
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
|
# Input
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.bank_i.reset.eq(self._r_reset_logic.storage),
|
|
||||||
self.bank_i.sel.eq(self._r_chan_sel.storage),
|
self.bank_i.sel.eq(self._r_chan_sel.storage),
|
||||||
self._r_i_timestamp.status.eq(self.bank_i.timestamp),
|
self._r_i_timestamp.status.eq(self.bank_i.timestamp),
|
||||||
self._r_i_value.status.eq(self.bank_i.value),
|
self._r_i_value.status.eq(self.bank_i.value),
|
||||||
self._r_i_readable.status.eq(self.bank_i.readable),
|
self._r_i_readable.status.eq(self.bank_i.readable),
|
||||||
self.bank_i.re.eq(self._r_i_re.re),
|
self.bank_i.re.eq(self._r_i_re.re),
|
||||||
self._r_i_overflow.status.eq(self.bank_i.overflow),
|
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._r_i_pileup_count.status.eq(self.bank_i.pileup_count),
|
||||||
self.bank_i.pileup_reset.eq(self._r_i_pileup_reset.re)
|
self.bank_i.pileup_reset.eq(self._r_i_pileup_reset.re)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Counter access
|
# Counter access
|
||||||
self.comb += reset_counter.eq(self._r_reset_counter.storage)
|
|
||||||
self.sync += \
|
self.sync += \
|
||||||
If(self._r_counter_update.re,
|
If(self._r_counter_update.re,
|
||||||
self._r_counter.status.eq(Cat(Replicate(0, fine_ts_width),
|
self._r_counter.status.eq(Cat(Replicate(0, fine_ts_width),
|
||||||
o_counter))
|
self.counter.o_value_sys))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Frequency
|
# Frequency
|
||||||
|
|
|
@ -5,25 +5,25 @@ from artiqlib.rtio.rbus import create_rbus
|
||||||
|
|
||||||
|
|
||||||
class SimplePHY(Module):
|
class SimplePHY(Module):
|
||||||
def __init__(self, pads, output_only_pads=set(), mini_pads=set()):
|
def __init__(self, pads, output_only_pads=set()):
|
||||||
self.rbus = create_rbus(0, pads, output_only_pads, mini_pads)
|
self.rbus = create_rbus(0, pads, output_only_pads)
|
||||||
self.loopback_latency = 3
|
self.loopback_latency = 3
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
for pad, chif in zip(pads, self.rbus):
|
for pad, chif in zip(pads, self.rbus):
|
||||||
o_pad = Signal()
|
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"):
|
if hasattr(chif, "oe"):
|
||||||
ts = TSTriple()
|
ts = TSTriple()
|
||||||
i_pad = Signal()
|
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.comb += ts.o.eq(o_pad)
|
||||||
self.specials += MultiReg(ts.i, i_pad), \
|
self.specials += MultiReg(ts.i, i_pad), \
|
||||||
ts.get_tristate(pad)
|
ts.get_tristate(pad)
|
||||||
|
|
||||||
i_pad_d = Signal()
|
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), \
|
self.comb += chif.i_stb.eq(i_pad ^ i_pad_d), \
|
||||||
chif.i_value.eq(i_pad)
|
chif.i_value.eq(i_pad)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -2,7 +2,7 @@ from migen.fhdl.std import *
|
||||||
from migen.genlib.record import Record
|
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 = []
|
rbus = []
|
||||||
for pad in pads:
|
for pad in pads:
|
||||||
layout = [
|
layout = [
|
||||||
|
@ -11,7 +11,7 @@ def create_rbus(fine_ts_bits, pads, output_only_pads, mini_pads):
|
||||||
]
|
]
|
||||||
if fine_ts_bits:
|
if fine_ts_bits:
|
||||||
layout.append(("o_fine_ts", 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 += [
|
layout += [
|
||||||
("oe", 1),
|
("oe", 1),
|
||||||
("i_stb", 1),
|
("i_stb", 1),
|
||||||
|
@ -20,9 +20,7 @@ def create_rbus(fine_ts_bits, pads, output_only_pads, mini_pads):
|
||||||
]
|
]
|
||||||
if fine_ts_bits:
|
if fine_ts_bits:
|
||||||
layout.append(("i_fine_ts", fine_ts_bits))
|
layout.append(("i_fine_ts", fine_ts_bits))
|
||||||
chif = Record(layout)
|
rbus.append(Record(layout))
|
||||||
chif.mini = pad in mini_pads
|
|
||||||
rbus.append(chif)
|
|
||||||
return rbus
|
return rbus
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,8 @@ long long int previous_fud_end_time;
|
||||||
void rtio_init(void)
|
void rtio_init(void)
|
||||||
{
|
{
|
||||||
previous_fud_end_time = 0;
|
previous_fud_end_time = 0;
|
||||||
rtio_reset_counter_write(1);
|
rtio_reset_write(1);
|
||||||
rtio_reset_logic_write(1);
|
rtio_reset_write(0);
|
||||||
rtio_reset_counter_write(0);
|
|
||||||
rtio_reset_logic_write(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rtio_oe(int channel, int oe)
|
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());
|
while(!rtio_o_writable_read());
|
||||||
rtio_o_we_write(1);
|
rtio_o_we_write(1);
|
||||||
if(rtio_o_underflow_read()) {
|
if(rtio_o_underflow_read()) {
|
||||||
rtio_reset_logic_write(1);
|
rtio_o_underflow_reset_write(1);
|
||||||
rtio_reset_logic_write(0);
|
|
||||||
exception_raise(EID_RTIO_UNDERFLOW);
|
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_value_write(value);
|
||||||
rtio_o_replace_write(1);
|
rtio_o_replace_write(1);
|
||||||
if(rtio_o_underflow_read()) {
|
if(rtio_o_underflow_read()) {
|
||||||
rtio_reset_logic_write(1);
|
rtio_o_underflow_reset_write(1);
|
||||||
rtio_reset_logic_write(0);
|
|
||||||
exception_raise(EID_RTIO_UNDERFLOW);
|
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)
|
long long int rtio_get_counter(void)
|
||||||
{
|
{
|
||||||
rtio_counter_update_write(1);
|
rtio_counter_update_write(1);
|
||||||
return rtio_counter_read();
|
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;
|
long long int r;
|
||||||
|
|
||||||
rtio_chan_sel_write(channel);
|
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()) {
|
if(rtio_i_overflow_read()) {
|
||||||
rtio_reset_logic_write(1);
|
rtio_i_overflow_reset_write(1);
|
||||||
rtio_reset_logic_write(0);
|
|
||||||
exception_raise(EID_RTIO_OVERFLOW);
|
exception_raise(EID_RTIO_OVERFLOW);
|
||||||
}
|
}
|
||||||
if(rtio_i_readable_read()) {
|
if(rtio_i_readable_read()) {
|
||||||
|
@ -93,7 +82,7 @@ int rtio_pileup_count(int channel)
|
||||||
|
|
||||||
void rtio_fud_sync(void)
|
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)
|
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_value_write(0);
|
||||||
rtio_o_we_write(1);
|
rtio_o_we_write(1);
|
||||||
if(rtio_o_underflow_read()) {
|
if(rtio_o_underflow_read()) {
|
||||||
rtio_reset_logic_write(1);
|
rtio_o_underflow_reset_write(1);
|
||||||
rtio_reset_logic_write(0);
|
|
||||||
exception_raise(EID_RTIO_UNDERFLOW);
|
exception_raise(EID_RTIO_UNDERFLOW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,8 @@ void rtio_init(void);
|
||||||
void rtio_oe(int channel, int oe);
|
void rtio_oe(int channel, int oe);
|
||||||
void rtio_set(long long int timestamp, int channel, int value);
|
void rtio_set(long long int timestamp, int channel, int value);
|
||||||
void rtio_replace(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_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);
|
int rtio_pileup_count(int channel);
|
||||||
|
|
||||||
void rtio_fud_sync(void);
|
void rtio_fud_sync(void);
|
||||||
|
|
|
@ -14,7 +14,6 @@ static const struct symbol syscalls[] = {
|
||||||
{"rtio_oe", rtio_oe},
|
{"rtio_oe", rtio_oe},
|
||||||
{"rtio_set", rtio_set},
|
{"rtio_set", rtio_set},
|
||||||
{"rtio_replace", rtio_replace},
|
{"rtio_replace", rtio_replace},
|
||||||
{"rtio_sync", rtio_sync},
|
|
||||||
{"rtio_get_counter", rtio_get_counter},
|
{"rtio_get_counter", rtio_get_counter},
|
||||||
{"rtio_get", rtio_get},
|
{"rtio_get", rtio_get},
|
||||||
{"rtio_pileup_count", rtio_pileup_count},
|
{"rtio_pileup_count", rtio_pileup_count},
|
||||||
|
|
|
@ -62,9 +62,10 @@ class ARTIQMiniSoC(BaseSoC):
|
||||||
rtio_pads.append(fud)
|
rtio_pads.append(fud)
|
||||||
self.submodules.rtiophy = rtio.phy.SimplePHY(
|
self.submodules.rtiophy = rtio.phy.SimplePHY(
|
||||||
rtio_pads,
|
rtio_pads,
|
||||||
output_only_pads={rtio_pads[1], rtio_pads[2], rtio_pads[3]},
|
output_only_pads={rtio_pads[1], rtio_pads[2], rtio_pads[3], fud})
|
||||||
mini_pads={fud})
|
|
||||||
self.submodules.rtio = rtio.RTIO(self.rtiophy, self.clk_freq)
|
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:
|
if with_test_gen:
|
||||||
self.submodules.test_gen = _TestGen(platform.request("ttl", 4))
|
self.submodules.test_gen = _TestGen(platform.request("ttl", 4))
|
||||||
|
|
Loading…
Reference in New Issue