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, 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()

View File

@ -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,

View File

@ -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):

View File

@ -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",

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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 */