From 1d34c06d797b9e9995f2d7f4a7765a36520313f6 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 29 Jul 2015 19:43:35 +0800 Subject: [PATCH] rtio: detect collision errors --- artiq/coredevice/runtime_exceptions.py | 20 +++++++++++-- artiq/gateware/rtio/core.py | 41 +++++++++++++++++++------- artiq/test/coredevice.py | 19 +++++++++++- examples/master/ddb.pyon | 1 + soc/runtime/exceptions.h | 5 ++-- soc/runtime/rtio.c | 22 ++++++++++++++ soc/runtime/rtio.h | 19 +++--------- 7 files changed, 97 insertions(+), 30 deletions(-) diff --git a/artiq/coredevice/runtime_exceptions.py b/artiq/coredevice/runtime_exceptions.py index aa9a2c1c5..e97152e71 100644 --- a/artiq/coredevice/runtime_exceptions.py +++ b/artiq/coredevice/runtime_exceptions.py @@ -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() diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index 08c46188f..7a087c7c3 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -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, diff --git a/artiq/test/coredevice.py b/artiq/test/coredevice.py index 7ae1dc336..530521a60 100644 --- a/artiq/test/coredevice.py +++ b/artiq/test/coredevice.py @@ -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): diff --git a/examples/master/ddb.pyon b/examples/master/ddb.pyon index a1ce5680a..3098391bd 100644 --- a/examples/master/ddb.pyon +++ b/examples/master/ddb.pyon @@ -135,6 +135,7 @@ "ttl_inout": "pmt0", "ttl_out": "ttl0", + "ttl_out_serdes": "ttl0", "pmt": "pmt0", "bd_dds": "dds0", diff --git a/soc/runtime/exceptions.h b/soc/runtime/exceptions.h index 1c820c060..ae97b9d95 100644 --- a/soc/runtime/exceptions.h +++ b/soc/runtime/exceptions.h @@ -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)); diff --git a/soc/runtime/rtio.c b/soc/runtime/rtio.c index 3329019ba..004c71a86 100644 --- a/soc/runtime/rtio.c +++ b/soc/runtime/rtio.c @@ -1,5 +1,6 @@ #include +#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); + } +} diff --git a/soc/runtime/rtio.h b/soc/runtime/rtio.h index be1591554..566f18ead 100644 --- a/soc/runtime/rtio.h +++ b/soc/runtime/rtio.h @@ -2,16 +2,17 @@ #define __RTIO_H #include -#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 */