forked from M-Labs/artiq
rtio: detect collision errors
This commit is contained in:
parent
b548d50a2f
commit
1d34c06d79
|
@ -41,6 +41,22 @@ class RTIOSequenceError(RuntimeException):
|
||||||
return "at {} on channel {}".format(self.p0*self.core.ref_period,
|
return "at {} on channel {}".format(self.p0*self.core.ref_period,
|
||||||
self.p1)
|
self.p1)
|
||||||
|
|
||||||
|
class RTIOCollisionError(RuntimeException):
|
||||||
|
"""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.
|
||||||
|
"""
|
||||||
|
eid = 5
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "at {} on channel {}".format(self.p0*self.core.ref_period,
|
||||||
|
self.p1)
|
||||||
|
|
||||||
|
|
||||||
class RTIOOverflow(RuntimeException):
|
class RTIOOverflow(RuntimeException):
|
||||||
"""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
|
||||||
|
@ -50,7 +66,7 @@ class RTIOOverflow(RuntimeException):
|
||||||
read attempt and discarding some events. Reading can be reattempted after
|
read attempt and discarding some events. Reading can be reattempted after
|
||||||
the exception is caught, and events will be partially retrieved.
|
the exception is caught, and events will be partially retrieved.
|
||||||
"""
|
"""
|
||||||
eid = 5
|
eid = 6
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "on channel {}".format(self.p0)
|
return "on channel {}".format(self.p0)
|
||||||
|
@ -60,7 +76,7 @@ class DDSBatchError(RuntimeException):
|
||||||
"""Raised when attempting to start a DDS batch while already in a batch,
|
"""Raised when attempting to start a DDS batch while already in a batch,
|
||||||
or when too many commands are batched.
|
or when too many commands are batched.
|
||||||
"""
|
"""
|
||||||
eid = 6
|
eid = 7
|
||||||
|
|
||||||
|
|
||||||
exception_map = {e.eid: e for e in globals().values()
|
exception_map = {e.eid: e for e in globals().values()
|
||||||
|
|
|
@ -100,6 +100,7 @@ class _OutputManager(Module):
|
||||||
|
|
||||||
self.underflow = Signal() # valid 1 cycle after we, pulsed
|
self.underflow = Signal() # valid 1 cycle after we, pulsed
|
||||||
self.sequence_error = Signal()
|
self.sequence_error = Signal()
|
||||||
|
self.collision_error = Signal()
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
|
@ -116,13 +117,24 @@ class _OutputManager(Module):
|
||||||
# Special cases
|
# Special cases
|
||||||
replace = Signal()
|
replace = Signal()
|
||||||
sequence_error = Signal()
|
sequence_error = Signal()
|
||||||
|
collision_error = Signal()
|
||||||
|
any_error = Signal()
|
||||||
nop = Signal()
|
nop = Signal()
|
||||||
self.sync.rsys += [
|
self.sync.rsys += [
|
||||||
replace.eq(self.ev.timestamp[fine_ts_width:] \
|
# Note: replace does not perform any RTLink address checks,
|
||||||
== buf.timestamp[fine_ts_width:]),
|
# i.e. a write to a different address will be silently replaced
|
||||||
sequence_error.eq(self.ev.timestamp[fine_ts_width:] \
|
# as well.
|
||||||
|
replace.eq(self.ev.timestamp == buf.timestamp),
|
||||||
|
# Detect sequence errors on coarse timestamps only
|
||||||
|
# so that they are mutually exclusive with collision errors.
|
||||||
|
sequence_error.eq(self.ev.timestamp[fine_ts_width:]
|
||||||
< buf.timestamp[fine_ts_width:])
|
< buf.timestamp[fine_ts_width:])
|
||||||
]
|
]
|
||||||
|
if fine_ts_width:
|
||||||
|
self.sync.rsys += collision_error.eq(
|
||||||
|
(self.ev.timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:])
|
||||||
|
& (self.ev.timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width]))
|
||||||
|
self.comb += any_error.eq(sequence_error | collision_error)
|
||||||
if interface.suppress_nop:
|
if interface.suppress_nop:
|
||||||
# disable NOP at reset: do not suppress a first write with all 0s
|
# disable NOP at reset: do not suppress a first write with all 0s
|
||||||
nop_en = Signal(reset=0)
|
nop_en = Signal(reset=0)
|
||||||
|
@ -134,11 +146,14 @@ class _OutputManager(Module):
|
||||||
if hasattr(self.ev, a)],
|
if hasattr(self.ev, a)],
|
||||||
default=0)),
|
default=0)),
|
||||||
# buf now contains valid data. enable NOP.
|
# buf now contains valid data. enable NOP.
|
||||||
If(self.we & ~sequence_error, nop_en.eq(1)),
|
If(self.we & ~any_error, nop_en.eq(1)),
|
||||||
# underflows cancel the write. allow it to be retried.
|
# underflows cancel the write. allow it to be retried.
|
||||||
If(self.underflow, nop_en.eq(0))
|
If(self.underflow, nop_en.eq(0))
|
||||||
]
|
]
|
||||||
self.comb += self.sequence_error.eq(self.we & sequence_error)
|
self.comb += [
|
||||||
|
self.sequence_error.eq(self.we & sequence_error),
|
||||||
|
self.collision_error.eq(self.we & collision_error)
|
||||||
|
]
|
||||||
|
|
||||||
# Buffer read and FIFO write
|
# Buffer read and FIFO write
|
||||||
self.comb += fifo.din.eq(buf)
|
self.comb += fifo.din.eq(buf)
|
||||||
|
@ -156,7 +171,7 @@ class _OutputManager(Module):
|
||||||
fifo.we.eq(1)
|
fifo.we.eq(1)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
If(self.we & ~replace & ~nop & ~sequence_error,
|
If(self.we & ~replace & ~nop & ~any_error,
|
||||||
fifo.we.eq(1)
|
fifo.we.eq(1)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -165,7 +180,7 @@ class _OutputManager(Module):
|
||||||
# Must come after read to handle concurrent read+write properly
|
# Must come after read to handle concurrent read+write properly
|
||||||
self.sync.rsys += [
|
self.sync.rsys += [
|
||||||
buf_just_written.eq(0),
|
buf_just_written.eq(0),
|
||||||
If(self.we & ~nop & ~sequence_error,
|
If(self.we & ~nop & ~any_error,
|
||||||
buf_just_written.eq(1),
|
buf_just_written.eq(1),
|
||||||
buf_pending.eq(1),
|
buf_pending.eq(1),
|
||||||
buf.eq(self.ev)
|
buf.eq(self.ev)
|
||||||
|
@ -286,9 +301,10 @@ class _KernelCSRs(AutoCSR):
|
||||||
self.o_address = CSRStorage(address_width)
|
self.o_address = CSRStorage(address_width)
|
||||||
self.o_timestamp = CSRStorage(full_ts_width)
|
self.o_timestamp = CSRStorage(full_ts_width)
|
||||||
self.o_we = CSR()
|
self.o_we = CSR()
|
||||||
self.o_status = CSRStatus(3)
|
self.o_status = CSRStatus(4)
|
||||||
self.o_underflow_reset = CSR()
|
self.o_underflow_reset = CSR()
|
||||||
self.o_sequence_error_reset = CSR()
|
self.o_sequence_error_reset = CSR()
|
||||||
|
self.o_collision_error_reset = CSR()
|
||||||
|
|
||||||
if data_width:
|
if data_width:
|
||||||
self.i_data = CSRStatus(data_width)
|
self.i_data = CSRStatus(data_width)
|
||||||
|
@ -369,17 +385,22 @@ class RTIO(Module):
|
||||||
|
|
||||||
underflow = Signal()
|
underflow = Signal()
|
||||||
sequence_error = Signal()
|
sequence_error = Signal()
|
||||||
|
collision_error = Signal()
|
||||||
self.sync.rsys += [
|
self.sync.rsys += [
|
||||||
If(selected & self.kcsrs.o_underflow_reset.re,
|
If(selected & self.kcsrs.o_underflow_reset.re,
|
||||||
underflow.eq(0)),
|
underflow.eq(0)),
|
||||||
If(selected & self.kcsrs.o_sequence_error_reset.re,
|
If(selected & self.kcsrs.o_sequence_error_reset.re,
|
||||||
sequence_error.eq(0)),
|
sequence_error.eq(0)),
|
||||||
|
If(selected & self.kcsrs.o_collision_error_reset.re,
|
||||||
|
collision_error.eq(0)),
|
||||||
If(o_manager.underflow, underflow.eq(1)),
|
If(o_manager.underflow, underflow.eq(1)),
|
||||||
If(o_manager.sequence_error, sequence_error.eq(1))
|
If(o_manager.sequence_error, sequence_error.eq(1)),
|
||||||
|
If(o_manager.collision_error, collision_error.eq(1))
|
||||||
]
|
]
|
||||||
o_statuses.append(Cat(~o_manager.writable,
|
o_statuses.append(Cat(~o_manager.writable,
|
||||||
underflow,
|
underflow,
|
||||||
sequence_error))
|
sequence_error,
|
||||||
|
collision_error))
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
@ -155,6 +155,19 @@ class SequenceError(EnvExperiment):
|
||||||
self.ttl_out.pulse(25*us)
|
self.ttl_out.pulse(25*us)
|
||||||
|
|
||||||
|
|
||||||
|
class CollisionError(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.attr_device("core")
|
||||||
|
self.attr_device("ttl_out_serdes")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def run(self):
|
||||||
|
delay(5*ms) # make sure we won't get underflow
|
||||||
|
for i in range(16):
|
||||||
|
self.ttl_out_serdes.pulse_mu(1)
|
||||||
|
delay_mu(1)
|
||||||
|
|
||||||
|
|
||||||
class TimeKeepsRunning(EnvExperiment):
|
class TimeKeepsRunning(EnvExperiment):
|
||||||
def build(self):
|
def build(self):
|
||||||
self.attr_device("core")
|
self.attr_device("core")
|
||||||
|
@ -211,7 +224,7 @@ class CoredeviceTest(ExperimentCase):
|
||||||
|
|
||||||
def test_loopback_count(self):
|
def test_loopback_count(self):
|
||||||
npulses = 2
|
npulses = 2
|
||||||
r = self.execute(LoopbackCount, npulses=npulses)
|
self.execute(LoopbackCount, npulses=npulses)
|
||||||
count = self.rdb.get("count")
|
count = self.rdb.get("count")
|
||||||
self.assertEqual(count, npulses)
|
self.assertEqual(count, npulses)
|
||||||
|
|
||||||
|
@ -223,6 +236,10 @@ class CoredeviceTest(ExperimentCase):
|
||||||
with self.assertRaises(runtime_exceptions.RTIOSequenceError):
|
with self.assertRaises(runtime_exceptions.RTIOSequenceError):
|
||||||
self.execute(SequenceError)
|
self.execute(SequenceError)
|
||||||
|
|
||||||
|
def test_collision_error(self):
|
||||||
|
with self.assertRaises(runtime_exceptions.RTIOCollisionError):
|
||||||
|
self.execute(CollisionError)
|
||||||
|
|
||||||
def test_watchdog(self):
|
def test_watchdog(self):
|
||||||
# watchdog only works on the device
|
# watchdog only works on the device
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
|
|
|
@ -135,6 +135,7 @@
|
||||||
|
|
||||||
"ttl_inout": "pmt0",
|
"ttl_inout": "pmt0",
|
||||||
"ttl_out": "ttl0",
|
"ttl_out": "ttl0",
|
||||||
|
"ttl_out_serdes": "ttl0",
|
||||||
|
|
||||||
"pmt": "pmt0",
|
"pmt": "pmt0",
|
||||||
"bd_dds": "dds0",
|
"bd_dds": "dds0",
|
||||||
|
|
|
@ -7,8 +7,9 @@ enum {
|
||||||
EID_RPC_EXCEPTION = 2,
|
EID_RPC_EXCEPTION = 2,
|
||||||
EID_RTIO_UNDERFLOW = 3,
|
EID_RTIO_UNDERFLOW = 3,
|
||||||
EID_RTIO_SEQUENCE_ERROR = 4,
|
EID_RTIO_SEQUENCE_ERROR = 4,
|
||||||
EID_RTIO_OVERFLOW = 5,
|
EID_RTIO_COLLISION_ERROR = 5,
|
||||||
EID_DDS_BATCH_ERROR = 6,
|
EID_RTIO_OVERFLOW = 6,
|
||||||
|
EID_DDS_BATCH_ERROR = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
int exception_setjmp(void *jb) __attribute__((returns_twice));
|
int exception_setjmp(void *jb) __attribute__((returns_twice));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <generated/csr.h>
|
#include <generated/csr.h>
|
||||||
|
|
||||||
|
#include "exceptions.h"
|
||||||
#include "rtio.h"
|
#include "rtio.h"
|
||||||
|
|
||||||
void rtio_init(void)
|
void rtio_init(void)
|
||||||
|
@ -14,3 +15,24 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rtio_process_exceptional_status(int status, long long int timestamp, int channel)
|
||||||
|
{
|
||||||
|
if(status & RTIO_O_STATUS_FULL)
|
||||||
|
while(rtio_o_status_read() & RTIO_O_STATUS_FULL);
|
||||||
|
if(status & RTIO_O_STATUS_UNDERFLOW) {
|
||||||
|
rtio_o_underflow_reset_write(1);
|
||||||
|
exception_raise_params(EID_RTIO_UNDERFLOW,
|
||||||
|
timestamp, channel, rtio_get_counter());
|
||||||
|
}
|
||||||
|
if(status & RTIO_O_STATUS_SEQUENCE_ERROR) {
|
||||||
|
rtio_o_sequence_error_reset_write(1);
|
||||||
|
exception_raise_params(EID_RTIO_SEQUENCE_ERROR,
|
||||||
|
timestamp, channel, 0);
|
||||||
|
}
|
||||||
|
if(status & RTIO_O_STATUS_COLLISION_ERROR) {
|
||||||
|
rtio_o_collision_error_reset_write(1);
|
||||||
|
exception_raise_params(EID_RTIO_COLLISION_ERROR,
|
||||||
|
timestamp, channel, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,16 +2,17 @@
|
||||||
#define __RTIO_H
|
#define __RTIO_H
|
||||||
|
|
||||||
#include <generated/csr.h>
|
#include <generated/csr.h>
|
||||||
#include "exceptions.h"
|
|
||||||
|
|
||||||
#define RTIO_O_STATUS_FULL 1
|
#define RTIO_O_STATUS_FULL 1
|
||||||
#define RTIO_O_STATUS_UNDERFLOW 2
|
#define RTIO_O_STATUS_UNDERFLOW 2
|
||||||
#define RTIO_O_STATUS_SEQUENCE_ERROR 4
|
#define RTIO_O_STATUS_SEQUENCE_ERROR 4
|
||||||
|
#define RTIO_O_STATUS_COLLISION_ERROR 8
|
||||||
#define RTIO_I_STATUS_EMPTY 1
|
#define RTIO_I_STATUS_EMPTY 1
|
||||||
#define RTIO_I_STATUS_OVERFLOW 2
|
#define RTIO_I_STATUS_OVERFLOW 2
|
||||||
|
|
||||||
void rtio_init(void);
|
void rtio_init(void);
|
||||||
long long int rtio_get_counter(void);
|
long long int rtio_get_counter(void);
|
||||||
|
void rtio_process_exceptional_status(int status, long long int timestamp, int channel);
|
||||||
|
|
||||||
static inline void rtio_write_and_process_status(long long int timestamp, int channel)
|
static inline void rtio_write_and_process_status(long long int timestamp, int channel)
|
||||||
{
|
{
|
||||||
|
@ -19,20 +20,8 @@ static inline void rtio_write_and_process_status(long long int timestamp, int ch
|
||||||
|
|
||||||
rtio_o_we_write(1);
|
rtio_o_we_write(1);
|
||||||
status = rtio_o_status_read();
|
status = rtio_o_status_read();
|
||||||
if(status) {
|
if(status)
|
||||||
if(status & RTIO_O_STATUS_FULL)
|
rtio_process_exceptional_status(status, timestamp, channel);
|
||||||
while(rtio_o_status_read() & RTIO_O_STATUS_FULL);
|
|
||||||
if(status & RTIO_O_STATUS_UNDERFLOW) {
|
|
||||||
rtio_o_underflow_reset_write(1);
|
|
||||||
exception_raise_params(EID_RTIO_UNDERFLOW,
|
|
||||||
timestamp, channel, rtio_get_counter());
|
|
||||||
}
|
|
||||||
if(status & RTIO_O_STATUS_SEQUENCE_ERROR) {
|
|
||||||
rtio_o_sequence_error_reset_write(1);
|
|
||||||
exception_raise_params(EID_RTIO_SEQUENCE_ERROR,
|
|
||||||
timestamp, channel, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __RTIO_H */
|
#endif /* __RTIO_H */
|
||||||
|
|
Loading…
Reference in New Issue