rtio: detect collision errors

This commit is contained in:
Sebastien Bourdeauducq 2015-07-29 19:43:35 +08:00
parent b548d50a2f
commit 1d34c06d79
7 changed files with 97 additions and 30 deletions

View File

@ -41,6 +41,22 @@ class RTIOSequenceError(RuntimeException):
return "at {} on channel {}".format(self.p0*self.core.ref_period,
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):
"""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
the exception is caught, and events will be partially retrieved.
"""
eid = 5
eid = 6
def __str__(self):
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,
or when too many commands are batched.
"""
eid = 6
eid = 7
exception_map = {e.eid: e for e in globals().values()

View File

@ -100,6 +100,7 @@ class _OutputManager(Module):
self.underflow = Signal() # valid 1 cycle after we, pulsed
self.sequence_error = Signal()
self.collision_error = Signal()
# # #
@ -116,13 +117,24 @@ class _OutputManager(Module):
# Special cases
replace = Signal()
sequence_error = Signal()
collision_error = Signal()
any_error = Signal()
nop = Signal()
self.sync.rsys += [
replace.eq(self.ev.timestamp[fine_ts_width:] \
== buf.timestamp[fine_ts_width:]),
sequence_error.eq(self.ev.timestamp[fine_ts_width:] \
# Note: replace does not perform any RTLink address checks,
# i.e. a write to a different address will be silently replaced
# 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:])
]
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:
# disable NOP at reset: do not suppress a first write with all 0s
nop_en = Signal(reset=0)
@ -134,11 +146,14 @@ class _OutputManager(Module):
if hasattr(self.ev, a)],
default=0)),
# 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.
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
self.comb += fifo.din.eq(buf)
@ -156,7 +171,7 @@ class _OutputManager(Module):
fifo.we.eq(1)
)
),
If(self.we & ~replace & ~nop & ~sequence_error,
If(self.we & ~replace & ~nop & ~any_error,
fifo.we.eq(1)
)
)
@ -165,7 +180,7 @@ class _OutputManager(Module):
# Must come after read to handle concurrent read+write properly
self.sync.rsys += [
buf_just_written.eq(0),
If(self.we & ~nop & ~sequence_error,
If(self.we & ~nop & ~any_error,
buf_just_written.eq(1),
buf_pending.eq(1),
buf.eq(self.ev)
@ -286,9 +301,10 @@ class _KernelCSRs(AutoCSR):
self.o_address = CSRStorage(address_width)
self.o_timestamp = CSRStorage(full_ts_width)
self.o_we = CSR()
self.o_status = CSRStatus(3)
self.o_status = CSRStatus(4)
self.o_underflow_reset = CSR()
self.o_sequence_error_reset = CSR()
self.o_collision_error_reset = CSR()
if data_width:
self.i_data = CSRStatus(data_width)
@ -369,17 +385,22 @@ class RTIO(Module):
underflow = Signal()
sequence_error = Signal()
collision_error = Signal()
self.sync.rsys += [
If(selected & self.kcsrs.o_underflow_reset.re,
underflow.eq(0)),
If(selected & self.kcsrs.o_sequence_error_reset.re,
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.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,
underflow,
sequence_error))
sequence_error,
collision_error))
if channel.interface.i is not None:
i_manager = _InputManager(channel.interface.i, self.counter,

View File

@ -155,6 +155,19 @@ class SequenceError(EnvExperiment):
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):
def build(self):
self.attr_device("core")
@ -211,7 +224,7 @@ class CoredeviceTest(ExperimentCase):
def test_loopback_count(self):
npulses = 2
r = self.execute(LoopbackCount, npulses=npulses)
self.execute(LoopbackCount, npulses=npulses)
count = self.rdb.get("count")
self.assertEqual(count, npulses)
@ -223,6 +236,10 @@ class CoredeviceTest(ExperimentCase):
with self.assertRaises(runtime_exceptions.RTIOSequenceError):
self.execute(SequenceError)
def test_collision_error(self):
with self.assertRaises(runtime_exceptions.RTIOCollisionError):
self.execute(CollisionError)
def test_watchdog(self):
# watchdog only works on the device
with self.assertRaises(IOError):

View File

@ -135,6 +135,7 @@
"ttl_inout": "pmt0",
"ttl_out": "ttl0",
"ttl_out_serdes": "ttl0",
"pmt": "pmt0",
"bd_dds": "dds0",

View File

@ -7,8 +7,9 @@ enum {
EID_RPC_EXCEPTION = 2,
EID_RTIO_UNDERFLOW = 3,
EID_RTIO_SEQUENCE_ERROR = 4,
EID_RTIO_OVERFLOW = 5,
EID_DDS_BATCH_ERROR = 6,
EID_RTIO_COLLISION_ERROR = 5,
EID_RTIO_OVERFLOW = 6,
EID_DDS_BATCH_ERROR = 7
};
int exception_setjmp(void *jb) __attribute__((returns_twice));

View File

@ -1,5 +1,6 @@
#include <generated/csr.h>
#include "exceptions.h"
#include "rtio.h"
void rtio_init(void)
@ -14,3 +15,24 @@ long long int rtio_get_counter(void)
rtio_counter_update_write(1);
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);
}
}

View File

@ -2,16 +2,17 @@
#define __RTIO_H
#include <generated/csr.h>
#include "exceptions.h"
#define RTIO_O_STATUS_FULL 1
#define RTIO_O_STATUS_UNDERFLOW 2
#define RTIO_O_STATUS_SEQUENCE_ERROR 4
#define RTIO_O_STATUS_COLLISION_ERROR 8
#define RTIO_I_STATUS_EMPTY 1
#define RTIO_I_STATUS_OVERFLOW 2
void rtio_init(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)
{
@ -19,20 +20,8 @@ static inline void rtio_write_and_process_status(long long int timestamp, int ch
rtio_o_we_write(1);
status = rtio_o_status_read();
if(status) {
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_process_exceptional_status(status, timestamp, channel);
}
#endif /* __RTIO_H */