make collision and busy asynchronous errors, and simplify CPU/gateware handshake for output errors and reads

This commit is contained in:
Sebastien Bourdeauducq 2017-03-27 16:32:23 +08:00
parent 1ee3f96482
commit b74d6fb9ba
14 changed files with 134 additions and 244 deletions

View File

@ -26,6 +26,8 @@ Release notes
* The kc705 gateware target has been renamed kc705_dds. * The kc705 gateware target has been renamed kc705_dds.
* ``artiq.coredevice.comm_tcp`` has been renamed ``artiq.coredevice.comm_kernel``, * ``artiq.coredevice.comm_tcp`` has been renamed ``artiq.coredevice.comm_kernel``,
and ``Comm`` has been renamed ``CommKernel``. and ``Comm`` has been renamed ``CommKernel``.
* The "collision" and "busy" RTIO errors are reported through the log instead of
raising exceptions.
2.2 2.2

View File

@ -1,11 +1,9 @@
from artiq.coredevice import exceptions, dds, spi from artiq.coredevice import exceptions, dds, spi
from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, RTIOOverflow)
RTIOCollision, RTIOOverflow, RTIOBusy)
from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE,
PHASE_MODE_TRACKING) PHASE_MODE_TRACKING)
__all__ = [] __all__ = []
__all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollision", __all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOOverflow"]
"RTIOOverflow", "RTIOBusy"]
__all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE",
"PHASE_MODE_TRACKING"] "PHASE_MODE_TRACKING"]

View File

@ -23,12 +23,15 @@ class ExceptionType(Enum):
legacy_reset_falling = 0b000001 legacy_reset_falling = 0b000001
legacy_reset_phy = 0b000010 legacy_reset_phy = 0b000010
legacy_reset_phy_falling = 0b000011 legacy_reset_phy_falling = 0b000011
legacy_o_underflow_reset = 0b010000
legacy_o_sequence_error_reset = 0b010001
legacy_o_collision_reset = 0b010010
legacy_i_overflow_reset = 0b100000
o_underflow_reset = 0b010000 o_underflow = 0b010100
o_sequence_error_reset = 0b010001 o_sequence_error = 0b010101
o_collision_reset = 0b010010
i_overflow_reset = 0b100000 i_overflow = 0b100001
def get_analyzer_dump(host, port=1382): def get_analyzer_dump(host, port=1382):

View File

@ -86,30 +86,6 @@ class RTIOSequenceError(Exception):
""" """
artiq_builtin = True artiq_builtin = True
class RTIOCollision(Exception):
"""Raised when an event is submitted on a given channel with the same
coarse timestamp as the previous one but with a different fine timestamp.
Coarse timestamps correspond to the RTIO system clock (typically around
125MHz) whereas fine timestamps correspond to the RTIO SERDES clock
(typically around 1GHz).
The offending event is discarded and the RTIO core keeps operating.
"""
artiq_builtin = True
class RTIOBusy(Exception):
"""Raised when at least one output event could not be executed because
the given channel was already busy executing a previous event.
This exception is raised late: after the error condition occurred. More
specifically it is raised on submitting an event on the same channel after
the execution of the faulty event was attempted.
The offending event was discarded.
"""
artiq_builtin = True
class RTIOOverflow(Exception): class RTIOOverflow(Exception):
"""Raised when at least one event could not be registered into the RTIO """Raised when at least one event could not be registered into the RTIO
input FIFO because it was full (CPU not reading fast enough). input FIFO because it was full (CPU not reading fast enough).

View File

@ -359,18 +359,6 @@ extern fn dma_playback(timestamp: i64, name: CSlice<u8>) {
"RTIO sequence error at {0} mu, channel {1}", "RTIO sequence error at {0} mu, channel {1}",
timestamp as i64, channel as i64, 0) timestamp as i64, channel as i64, 0)
} }
if status & rtio::RTIO_O_STATUS_COLLISION != 0 {
csr::rtio_dma::error_collision_reset_write(1);
raise!("RTIOCollision",
"RTIO collision at {0} mu, channel {1}",
timestamp as i64, channel as i64, 0)
}
if status & rtio::RTIO_O_STATUS_BUSY != 0 {
csr::rtio_dma::error_busy_reset_write(1);
raise!("RTIOBusy",
"RTIO busy on channel {0}",
channel as i64, 0, 0)
}
} }
} }

View File

@ -7,8 +7,6 @@ use kernel_proto::*;
pub const RTIO_O_STATUS_WAIT: u32 = 1; pub const RTIO_O_STATUS_WAIT: u32 = 1;
pub const RTIO_O_STATUS_UNDERFLOW: u32 = 2; pub const RTIO_O_STATUS_UNDERFLOW: u32 = 2;
pub const RTIO_O_STATUS_SEQUENCE_ERROR: u32 = 4; pub const RTIO_O_STATUS_SEQUENCE_ERROR: u32 = 4;
pub const RTIO_O_STATUS_COLLISION: u32 = 8;
pub const RTIO_O_STATUS_BUSY: u32 = 16;
pub const RTIO_I_STATUS_WAIT_EVENT: u32 = 1; pub const RTIO_I_STATUS_WAIT_EVENT: u32 = 1;
pub const RTIO_I_STATUS_OVERFLOW: u32 = 2; pub const RTIO_I_STATUS_OVERFLOW: u32 = 2;
pub const RTIO_I_STATUS_WAIT_STATUS: u32 = 4; pub const RTIO_I_STATUS_WAIT_STATUS: u32 = 4;
@ -43,29 +41,15 @@ unsafe fn process_exceptional_status(timestamp: i64, channel: i32, status: u32)
while csr::rtio::o_status_read() & RTIO_O_STATUS_WAIT != 0 {} while csr::rtio::o_status_read() & RTIO_O_STATUS_WAIT != 0 {}
} }
if status & RTIO_O_STATUS_UNDERFLOW != 0 { if status & RTIO_O_STATUS_UNDERFLOW != 0 {
csr::rtio::o_underflow_reset_write(1);
raise!("RTIOUnderflow", raise!("RTIOUnderflow",
"RTIO underflow at {0} mu, channel {1}, slack {2} mu", "RTIO underflow at {0} mu, channel {1}, slack {2} mu",
timestamp, channel as i64, timestamp - get_counter()) timestamp, channel as i64, timestamp - get_counter())
} }
if status & RTIO_O_STATUS_SEQUENCE_ERROR != 0 { if status & RTIO_O_STATUS_SEQUENCE_ERROR != 0 {
csr::rtio::o_sequence_error_reset_write(1);
raise!("RTIOSequenceError", raise!("RTIOSequenceError",
"RTIO sequence error at {0} mu, channel {1}", "RTIO sequence error at {0} mu, channel {1}",
timestamp, channel as i64, 0) timestamp, channel as i64, 0)
} }
if status & RTIO_O_STATUS_COLLISION != 0 {
csr::rtio::o_collision_reset_write(1);
raise!("RTIOCollision",
"RTIO collision at {0} mu, channel {1}",
timestamp, channel as i64, 0)
}
if status & RTIO_O_STATUS_BUSY != 0 {
csr::rtio::o_busy_reset_write(1);
raise!("RTIOBusy",
"RTIO busy on channel {0}",
channel as i64, 0, 0)
}
} }
pub extern fn output(timestamp: i64, channel: i32, addr: i32, data: i32) { pub extern fn output(timestamp: i64, channel: i32, addr: i32, data: i32) {
@ -118,9 +102,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> u64 {
return !0 return !0
} }
let timestamp = csr::rtio::i_timestamp_read(); csr::rtio::i_timestamp_read()
csr::rtio::i_re_write(1);
timestamp
} }
} }
@ -142,9 +124,7 @@ pub extern fn input_data(channel: i32) -> i32 {
channel as i64, 0, 0); channel as i64, 0, 0);
} }
let data = rtio_i_data_read(0); rtio_i_data_read(0) as i32
csr::rtio::i_re_write(1);
data as i32
} }
} }

View File

@ -127,8 +127,10 @@ class RTController(Module):
o_sequence_error_set = Signal() o_sequence_error_set = Signal()
o_underflow_set = Signal() o_underflow_set = Signal()
self.sync.sys_with_rst += [ self.sync.sys_with_rst += [
If(self.cri.cmd == cri.commands["o_underflow_reset"], o_status_underflow.eq(0)), If(self.cri.cmd == cri.commands["write"],
If(self.cri.cmd == cri.commands["o_sequence_error_reset"], o_status_sequence_error.eq(0)), o_status_underflow.eq(0),
o_status_sequence_error.eq(0),
),
If(o_underflow_set, o_status_underflow.eq(1)), If(o_underflow_set, o_status_underflow.eq(1)),
If(o_sequence_error_set, o_status_sequence_error.eq(1)) If(o_sequence_error_set, o_status_sequence_error.eq(1))
] ]
@ -184,7 +186,7 @@ class RTController(Module):
NextState("WRITE") NextState("WRITE")
) )
), ),
If(self.cri.cmd == cri.commands["read_request"], NextState("READ")), If(self.cri.cmd == cri.commands["read"], NextState("READ")),
If(self.csrs.o_get_fifo_space.re, NextState("GET_FIFO_SPACE")) If(self.csrs.o_get_fifo_space.re, NextState("GET_FIFO_SPACE"))
) )
fsm.act("WRITE", fsm.act("WRITE",

View File

@ -51,6 +51,17 @@ class MessageEncoder(Module, AutoCSR):
# # # # # #
read_wait_event = cri.i_status[2]
read_wait_event_r = Signal()
read_done = Signal()
read_overflow = Signal()
self.sync += read_wait_event_r.eq(read_wait_event)
self.comb += \
If(read_wait_event_r & ~read_wait_event,
If(~cri.i_status[0], read_done.eq(1)),
If(cri.i_status[1], read_overflow.eq(1))
)
input_output_stb = Signal() input_output_stb = Signal()
input_output = Record(input_output_layout) input_output = Record(input_output_layout)
self.comb += [ self.comb += [
@ -66,8 +77,7 @@ class MessageEncoder(Module, AutoCSR):
input_output.timestamp.eq(cri.i_timestamp), input_output.timestamp.eq(cri.i_timestamp),
input_output.data.eq(cri.i_data) input_output.data.eq(cri.i_data)
), ),
input_output_stb.eq((cri.cmd == cri_commands["write"]) | input_output_stb.eq((cri.cmd == cri_commands["write"]) | read_done)
(cri.cmd == cri_commands["read"]))
] ]
exception_stb = Signal() exception_stb = Signal()
@ -77,14 +87,22 @@ class MessageEncoder(Module, AutoCSR):
exception.channel.eq(cri.chan_sel), exception.channel.eq(cri.chan_sel),
exception.rtio_counter.eq(cri.counter), exception.rtio_counter.eq(cri.counter),
] ]
for ename in ("o_underflow_reset", "o_sequence_error_reset", just_written = Signal()
"o_collision_reset", "i_overflow_reset"): self.sync += just_written.eq(cri.cmd == cri_commands["write"])
self.comb += \ self.comb += [
If(cri.cmd == cri_commands[ename], If(just_written & cri.o_status[1],
exception_stb.eq(1), exception_stb.eq(1),
exception.exception_type.eq( exception.exception_type.eq(ExceptionType.o_underflow.value)
getattr(ExceptionType, ename).value) ),
If(just_written & cri.o_status[2],
exception_stb.eq(1),
exception.exception_type.eq(ExceptionType.o_sequence_error.value)
),
If(read_overflow,
exception_stb.eq(1),
exception.exception_type.eq(ExceptionType.i_overflow.value)
) )
]
stopped = Record(stopped_layout) stopped = Record(stopped_layout)
self.comb += [ self.comb += [

View File

@ -290,6 +290,7 @@ class Core(Module, AutoCSR):
self.cri = cri.Interface() self.cri = cri.Interface()
self.reset = CSR() self.reset = CSR()
self.reset_phy = CSR() self.reset_phy = CSR()
self.async_error = CSR(2)
self.comb += self.cri.arb_gnt.eq(1) self.comb += self.cri.arb_gnt.eq(1)
# Clocking/Reset # Clocking/Reset
@ -321,11 +322,31 @@ class Core(Module, AutoCSR):
# Managers # Managers
self.submodules.counter = RTIOCounter(len(self.cri.timestamp) - fine_ts_width) self.submodules.counter = RTIOCounter(len(self.cri.timestamp) - fine_ts_width)
i_datas, i_timestamps = [], [] # Collision is not an asynchronous error with local RTIO, but
# we treat it as such for consistency with DRTIO, where collisions
# are reported by the satellites.
o_underflow = Signal()
o_sequence_error = Signal()
o_collision = Signal()
o_busy = Signal()
self.sync.rsys += [
If(self.cri.cmd == cri.commands["write"],
o_underflow.eq(0),
o_sequence_error.eq(0),
),
If(self.async_error.re,
If(self.async_error.r[0], o_collision.eq(0)),
If(self.async_error.r[1], o_busy.eq(0)),
)
]
o_statuses, i_statuses = [], [] o_statuses, i_statuses = [], []
i_datas, i_timestamps = [], []
i_ack = Signal()
sel = self.cri.chan_sel[:16] sel = self.cri.chan_sel[:16]
for n, channel in enumerate(channels): for n, channel in enumerate(channels):
if isinstance(channel, LogChannel): if isinstance(channel, LogChannel):
o_statuses.append(1)
i_datas.append(0) i_datas.append(0)
i_timestamps.append(0) i_timestamps.append(0)
i_statuses.append(0) i_statuses.append(0)
@ -347,29 +368,13 @@ class Core(Module, AutoCSR):
self.comb += o_manager.we.eq(selected & (self.cri.cmd == cri.commands["write"])) self.comb += o_manager.we.eq(selected & (self.cri.cmd == cri.commands["write"]))
underflow = Signal()
sequence_error = Signal()
collision = Signal()
busy = Signal()
self.sync.rsys += [ self.sync.rsys += [
If(self.cri.cmd == cri.commands["o_underflow_reset"], If(o_manager.underflow, o_underflow.eq(1)),
underflow.eq(0)), If(o_manager.sequence_error, o_sequence_error.eq(1)),
If(self.cri.cmd == cri.commands["o_sequence_error_reset"], If(o_manager.collision, o_collision.eq(1)),
sequence_error.eq(0)), If(o_manager.busy, o_busy.eq(1))
If(self.cri.cmd == cri.commands["o_collision_reset"],
collision.eq(0)),
If(self.cri.cmd == cri.commands["o_busy_reset"],
busy.eq(0)),
If(o_manager.underflow, underflow.eq(1)),
If(o_manager.sequence_error, sequence_error.eq(1)),
If(o_manager.collision, collision.eq(1)),
If(o_manager.busy, busy.eq(1))
] ]
o_statuses.append(Cat(~o_manager.writable, o_statuses.append(o_manager.writable)
underflow,
sequence_error,
collision,
busy))
if channel.interface.i is not None: if channel.interface.i is not None:
i_manager = _InputManager(channel.interface.i, self.counter, i_manager = _InputManager(channel.interface.i, self.counter,
@ -386,42 +391,49 @@ class Core(Module, AutoCSR):
else: else:
i_timestamps.append(0) i_timestamps.append(0)
self.comb += i_manager.re.eq(selected & (self.cri.cmd == cri.commands["read"]))
overflow = Signal() overflow = Signal()
self.sync.rsys += [ self.sync.rsys += [
If(selected & (self.cri.cmd == cri.commands["i_overflow_reset"]), If(selected & i_ack,
overflow.eq(0)), overflow.eq(0)),
If(i_manager.overflow, If(i_manager.overflow,
overflow.eq(1)) overflow.eq(1))
] ]
i_statuses.append(Cat(i_manager.readable, overflow)) self.comb += i_manager.re.eq(selected & i_ack & ~overflow)
i_statuses.append(Cat(i_manager.readable & ~overflow, overflow))
else: else:
i_datas.append(0) i_datas.append(0)
i_timestamps.append(0) i_timestamps.append(0)
i_statuses.append(0) i_statuses.append(0)
i_status_raw = Signal(2) o_status_raw = Signal()
self.sync.rsys += i_status_raw.eq(Array(i_statuses)[sel]) self.comb += [
o_status_raw.eq(Array(o_statuses)[sel]),
self.cri.o_status.eq(Cat(
~o_status_raw, o_underflow, o_sequence_error)),
self.async_error.w.eq(Cat(o_collision, o_busy))
]
i_status_raw = Signal(2)
self.comb += i_status_raw.eq(Array(i_statuses)[sel])
input_timeout = Signal.like(self.cri.timestamp) input_timeout = Signal.like(self.cri.timestamp)
input_pending = Signal() input_pending = Signal()
self.sync.rsys += [ self.sync.rsys += [
i_ack.eq(0),
If(i_ack,
self.cri.i_status.eq(Cat(~i_status_raw[0], i_status_raw[1], 0)),
self.cri.i_data.eq(Array(i_datas)[sel]),
self.cri.i_timestamp.eq(Array(i_timestamps)[sel]),
),
If((self.cri.counter >= input_timeout) | (i_status_raw != 0), If((self.cri.counter >= input_timeout) | (i_status_raw != 0),
If(input_pending, i_ack.eq(1)),
input_pending.eq(0) input_pending.eq(0)
), ),
If(self.cri.cmd == cri.commands["read_request"], If(self.cri.cmd == cri.commands["read"],
input_timeout.eq(self.cri.timestamp), input_timeout.eq(self.cri.timestamp),
input_pending.eq(1) input_pending.eq(1),
self.cri.i_status.eq(0b100)
) )
] ]
self.comb += [ self.comb += self.cri.counter.eq(self.counter.value_sys << fine_ts_width)
self.cri.i_data.eq(Array(i_datas)[sel]),
self.cri.i_timestamp.eq(Array(i_timestamps)[sel]),
self.cri.o_status.eq(Array(o_statuses)[sel]),
self.cri.i_status.eq(Cat(~i_status_raw[0], i_status_raw[1], input_pending)),
self.cri.counter.eq(self.counter.value_sys << fine_ts_width)
]

View File

@ -12,15 +12,7 @@ commands = {
"write": 1, "write": 1,
# i_status should have the "wait for status" bit set until # i_status should have the "wait for status" bit set until
# an event is available, or timestamp is reached. # an event is available, or timestamp is reached.
"read_request": 2, "read": 2
# consume the read event
"read": 3,
"o_underflow_reset": 4,
"o_sequence_error_reset": 5,
"o_collision_reset": 6,
"o_busy_reset": 7,
"i_overflow_reset": 8
} }
@ -28,7 +20,7 @@ layout = [
("arb_req", 1, DIR_M_TO_S), ("arb_req", 1, DIR_M_TO_S),
("arb_gnt", 1, DIR_S_TO_M), ("arb_gnt", 1, DIR_S_TO_M),
("cmd", 4, DIR_M_TO_S), ("cmd", 2, DIR_M_TO_S),
# 8 MSBs of chan_sel are used to select core # 8 MSBs of chan_sel are used to select core
("chan_sel", 24, DIR_M_TO_S), ("chan_sel", 24, DIR_M_TO_S),
("timestamp", 64, DIR_M_TO_S), ("timestamp", 64, DIR_M_TO_S),
@ -36,13 +28,14 @@ layout = [
("o_data", 512, DIR_M_TO_S), ("o_data", 512, DIR_M_TO_S),
("o_address", 16, DIR_M_TO_S), ("o_address", 16, DIR_M_TO_S),
# o_status bits: # o_status bits:
# <0:wait> <1:underflow> <2:sequence_error> <3:collision> <4:busy> # <0:wait> <1:underflow> <2:sequence_error>
("o_status", 5, DIR_S_TO_M), ("o_status", 3, DIR_S_TO_M),
("i_data", 32, DIR_S_TO_M), ("i_data", 32, DIR_S_TO_M),
("i_timestamp", 64, DIR_S_TO_M), ("i_timestamp", 64, DIR_S_TO_M),
# i_status bits: # i_status bits:
# <0:wait for event (command timeout)> <1:overflow> <2:wait for status> # <0:wait for event (command timeout)> <1:overflow> <2:wait for status>
# <0> and <1> are mutually exclusive. <1> has higher priority.
("i_status", 3, DIR_S_TO_M), ("i_status", 3, DIR_S_TO_M),
("counter", 64, DIR_S_TO_M) ("counter", 64, DIR_S_TO_M)
@ -66,16 +59,11 @@ class KernelInitiator(Module, AutoCSR):
self.o_data = CSRStorage(512, write_from_dev=True) self.o_data = CSRStorage(512, write_from_dev=True)
self.o_address = CSRStorage(16) self.o_address = CSRStorage(16)
self.o_we = CSR() self.o_we = CSR()
self.o_status = CSRStatus(5) self.o_status = CSRStatus(3)
self.o_underflow_reset = CSR()
self.o_sequence_error_reset = CSR()
self.o_collision_reset = CSR()
self.o_busy_reset = CSR()
self.i_data = CSRStatus(32) self.i_data = CSRStatus(32)
self.i_timestamp = CSRStatus(64) self.i_timestamp = CSRStatus(64)
self.i_request = CSR() self.i_request = CSR()
self.i_re = CSR()
self.i_status = CSRStatus(3) self.i_status = CSRStatus(3)
self.i_overflow_reset = CSR() self.i_overflow_reset = CSR()
@ -94,13 +82,7 @@ class KernelInitiator(Module, AutoCSR):
self.cri.cmd.eq(commands["nop"]), self.cri.cmd.eq(commands["nop"]),
If(self.o_we.re, self.cri.cmd.eq(commands["write"])), If(self.o_we.re, self.cri.cmd.eq(commands["write"])),
If(self.i_request.re, self.cri.cmd.eq(commands["read_request"])), If(self.i_request.re, self.cri.cmd.eq(commands["read"])),
If(self.i_re.re, self.cri.cmd.eq(commands["read"])),
If(self.o_underflow_reset.re, self.cri.cmd.eq(commands["o_underflow_reset"])),
If(self.o_sequence_error_reset.re, self.cri.cmd.eq(commands["o_sequence_error_reset"])),
If(self.o_collision_reset.re, self.cri.cmd.eq(commands["o_collision_reset"])),
If(self.o_busy_reset.re, self.cri.cmd.eq(commands["o_busy_reset"])),
If(self.i_overflow_reset.re, self.cri.cmd.eq(commands["i_overflow_reset"])),
self.cri.chan_sel.eq(self.chan_sel.storage), self.cri.chan_sel.eq(self.chan_sel.storage),
self.cri.timestamp.eq(self.timestamp.storage), self.cri.timestamp.eq(self.timestamp.storage),

View File

@ -247,11 +247,9 @@ class CRIMaster(Module, AutoCSR):
self.arb_req = CSRStorage() self.arb_req = CSRStorage()
self.arb_gnt = CSRStatus() self.arb_gnt = CSRStatus()
self.error_status = CSRStatus(5) # same encoding as RTIO status self.error_status = CSRStatus(3) # same encoding as RTIO status
self.error_underflow_reset = CSR() self.error_underflow_reset = CSR()
self.error_sequence_error_reset = CSR() self.error_sequence_error_reset = CSR()
self.error_collision_reset = CSR()
self.error_busy_reset = CSR()
self.error_channel = CSRStatus(24) self.error_channel = CSRStatus(24)
self.error_timestamp = CSRStatus(64) self.error_timestamp = CSRStatus(64)
@ -268,9 +266,8 @@ class CRIMaster(Module, AutoCSR):
self.arb_gnt.status.eq(self.cri.arb_gnt) self.arb_gnt.status.eq(self.cri.arb_gnt)
] ]
error_set = Signal(4) error_set = Signal(2)
for i, rcsr in enumerate([self.error_underflow_reset, self.error_sequence_error_reset, for i, rcsr in enumerate([self.error_underflow_reset, self.error_sequence_error_reset]):
self.error_collision_reset, self.error_busy_reset]):
# bit 0 is RTIO wait and always 0 here # bit 0 is RTIO wait and always 0 here
bit = i + 1 bit = i + 1
self.sync += [ self.sync += [
@ -320,16 +317,12 @@ class CRIMaster(Module, AutoCSR):
NextState("IDLE") NextState("IDLE")
), ),
If(self.cri.o_status[1], NextState("UNDERFLOW")), If(self.cri.o_status[1], NextState("UNDERFLOW")),
If(self.cri.o_status[2], NextState("SEQUENCE_ERROR")), If(self.cri.o_status[2], NextState("SEQUENCE_ERROR"))
If(self.cri.o_status[3], NextState("COLLISION")),
If(self.cri.o_status[4], NextState("BUSY"))
) )
for n, name in enumerate(["UNDERFLOW", "SEQUENCE_ERROR", for n, name in enumerate(["UNDERFLOW", "SEQUENCE_ERROR"]):
"COLLISION", "BUSY"]):
fsm.act(name, fsm.act(name,
self.busy.eq(1), self.busy.eq(1),
error_set.eq(1 << n), error_set.eq(1 << n),
self.cri.cmd.eq(cri.commands["o_" + name.lower() + "_reset"]),
self.sink.ack.eq(1), self.sink.ack.eq(1),
NextState("IDLE") NextState("IDLE")
) )

View File

@ -384,13 +384,14 @@ class CoredeviceTest(ExperimentCase):
with self.assertRaises(RTIOSequenceError): with self.assertRaises(RTIOSequenceError):
self.execute(SequenceError) self.execute(SequenceError)
def test_collision(self): # TODO: capture core device log
with self.assertRaises(RTIOCollision): # def test_collision(self):
self.execute(Collision) # with self.assertRaises(RTIOCollision):
# self.execute(Collision)
def test_address_collision(self): # def test_address_collision(self):
with self.assertRaises(RTIOCollision): # with self.assertRaises(RTIOCollision):
self.execute(AddressCollision) # self.execute(AddressCollision)
def test_watchdog(self): def test_watchdog(self):
# watchdog only works on the device # watchdog only works on the device

View File

@ -1,84 +0,0 @@
from artiq.experiment import *
from artiq.test.hardware_testbench import ExperimentCase
class WrongError(Exception):
pass
class Collision(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("spi0")
@kernel
def run(self):
self.core.reset()
t = now_mu()
try:
self.spi0.set_config_mu()
except RTIOBusy:
raise WrongError()
at_mu(t)
self.spi0.set_config_mu()
class Busy(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("spi0")
self.setattr_device("led")
@kernel
def run(self):
self.core.reset()
try:
self.spi0.set_config_mu()
t = now_mu()
self.spi0.set_config_mu()
at_mu(t + self.spi0.ref_period_mu)
self.spi0.set_config_mu() # causes the error
self.led.on()
self.led.sync() # registers the error
self.core.break_realtime()
except RTIOBusy:
raise WrongError() # we don't expect RTIOBusy so far
self.spi0.set_config_mu() # raises the error
class DrainErrors(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("spi0")
self.setattr_device("led")
@kernel
def run(self):
self.core.reset()
while True:
try:
self.core.break_realtime()
delay(100*us)
self.spi0.set_config_mu()
self.led.on()
self.led.sync()
self.core.break_realtime()
self.spi0.set_config_mu()
self.led.off()
return
except:
pass
class SPITest(ExperimentCase):
def tearDown(self):
self.execute(DrainErrors)
ExperimentCase.tearDown(self)
def test_collision(self):
with self.assertRaises(RTIOCollision):
self.execute(Collision)
def test_busy(self):
with self.assertRaises(RTIOBusy):
self.execute(Busy)

View File

@ -117,6 +117,25 @@ To track down ``RTIOUnderflows`` in an experiment there are a few approaches:
code. code.
* The :any:`integrated logic analyzer <core-device-rtio-analyzer-tool>` shows the timeline context that lead to the exception. The analyzer is always active and supports plotting of RTIO slack. RTIO slack is the difference between timeline cursor and wall clock time (``now - rtio_counter``). * The :any:`integrated logic analyzer <core-device-rtio-analyzer-tool>` shows the timeline context that lead to the exception. The analyzer is always active and supports plotting of RTIO slack. RTIO slack is the difference between timeline cursor and wall clock time (``now - rtio_counter``).
Collisions
----------
A collision happens when more than one event is submitted on a given channel with the same coarse timestamp, and that channel does not implement replacement behavior or the fine timestamps are different.
Coarse timestamps correspond to the RTIO system clock (typically around 125MHz) whereas fine timestamps correspond to the RTIO SERDES clock (typically around 1GHz). Different channels may have different ratios between the coarse and fine timestamp clock frequencies.
The offending event is discarded and the RTIO core keeps operating.
This error is reported asynchronously via the core device log: for performance reasons with DRTIO, the CPU does not wait for an error report from the satellite after writing an event. Therefore, it is not possible to raise an exception precisely.
Busy errors
-----------
A busy error happens when at least one output event could not be executed because the channel was already busy executing a previous event.
The offending event was discarded.
This error is reported asynchronously via the core device log.
Input channels and events Input channels and events
------------------------- -------------------------