From 2cb58592fff4bab2028081b2432794042efcc66b Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Tue, 8 Mar 2016 18:04:34 +0100 Subject: [PATCH 01/13] rtio: add RTIOBusy --- artiq/coredevice/__init__.py | 2 +- artiq/coredevice/exceptions.py | 13 +++++++++++++ artiq/gateware/rtio/core.py | 27 ++++++++++++++++++++++++--- artiq/runtime/rtio.c | 6 ++++++ artiq/runtime/rtio.h | 2 ++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index da8c72b1d..4eac74667 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -1,6 +1,6 @@ from artiq.coredevice import exceptions, dds, spi from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError, - RTIOCollision, RTIOOverflow, + RTIOCollision, RTIOOverflow, RTIOBusy, DDSBatchError, CacheError) from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, PHASE_MODE_TRACKING) diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index aa00ad784..987f5a127 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -98,6 +98,19 @@ class RTIOCollision(Exception): """ artiq_builtin = True +class RTIOBusy(Exception): + """Raised when an event could not be executed because the given channel + was already busy executing a previous event. The two events were not + sufficiently spaced on the timeline. + + This exception is raised after the error condition appeared. 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 is discarded and the RTIO core keeps operating. + """ + artiq_builtin = True + class RTIOOverflow(Exception): """Raised when at least one event could not be registered into the RTIO input FIFO because it was full (CPU not reading fast enough). diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index 8c2d1b237..cdc36598d 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -104,6 +104,7 @@ class _OutputManager(Module): self.underflow = Signal() # valid 1 cycle after we, pulsed self.sequence_error = Signal() self.collision = Signal() + self.busy = Signal() # pulsed # # # @@ -223,6 +224,20 @@ class _OutputManager(Module): dout.timestamp[fine_ts_width:] == counter.value_rtio), interface.stb.eq(dout_stb & dout_ack) ] + busy_sync = PulseSynchronizer("rio", "rsys") + busy_ack_sync = PulseSynchronizer("rsys", "rio") + self.submodules += busy_sync, busy_ack_sync + busy_blind = Signal() + self.comb += busy_sync.i.eq(interface.stb & interface.busy & ~busy_blind) + self.sync.rio += [ + If(interface.stb & interface.busy, busy_blind.eq(1)), + If(busy_ack_sync.o, busy_blind.eq(0)) + ] + self.comb += [ + busy_ack_sync.i.eq(busy_sync.o), + self.busy.eq(busy_sync.o) + ] + if data_width: self.comb += interface.data.eq(dout.data) if address_width: @@ -336,10 +351,11 @@ class _KernelCSRs(AutoCSR): self.o_address = CSRStorage(address_width) self.o_timestamp = CSRStorage(full_ts_width) self.o_we = CSR() - self.o_status = CSRStatus(4) + self.o_status = CSRStatus(5) self.o_underflow_reset = CSR() self.o_sequence_error_reset = CSR() self.o_collision_reset = CSR() + self.o_busy_reset = CSR() if data_width: self.i_data = CSRStatus(data_width) @@ -427,6 +443,7 @@ class RTIO(Module): underflow = Signal() sequence_error = Signal() collision = Signal() + busy = Signal() self.sync.rsys += [ If(selected & self.kcsrs.o_underflow_reset.re, underflow.eq(0)), @@ -434,14 +451,18 @@ class RTIO(Module): sequence_error.eq(0)), If(selected & self.kcsrs.o_collision_reset.re, collision.eq(0)), + If(selected & self.kcsrs.o_busy_reset.re, + 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.collision, collision.eq(1)), + If(o_manager.busy, busy.eq(1)) ] o_statuses.append(Cat(~o_manager.writable, underflow, sequence_error, - collision)) + collision, + busy)) if channel.interface.i is not None: i_manager = _InputManager(channel.interface.i, self.counter, diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 90dd7fb5e..385011cf5 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -39,6 +39,12 @@ static void rtio_process_exceptional_status( "RTIO collision at {0} mu, channel {1}", timestamp, channel, 0); } + if(status & RTIO_O_STATUS_BUSY) { + rtio_o_busy_reset_write(1); + artiq_raise_from_c("RTIOBusy", + "RTIO busy at {0} mu, channel {1}", + timestamp, channel, 0); + } } diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index c32dca484..cdaf8a6e1 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -2,11 +2,13 @@ #define __RTIO_H #include +#include "generated/csr.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 8 +#define RTIO_O_STATUS_BUSY 16 #define RTIO_I_STATUS_EMPTY 1 #define RTIO_I_STATUS_OVERFLOW 2 From b0de9ee90a5c54c80910dc314dfa8b14280448b2 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 12:27:45 +0100 Subject: [PATCH 02/13] coredevice: add RTIOBusy to __all__ --- artiq/coredevice/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/__init__.py b/artiq/coredevice/__init__.py index 4eac74667..9ea9f5c33 100644 --- a/artiq/coredevice/__init__.py +++ b/artiq/coredevice/__init__.py @@ -7,6 +7,6 @@ from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE, __all__ = [] __all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollision", - "RTIOOverflow", "DDSBatchError", "CacheError"] + "RTIOOverflow", "RTIOBusy", "DDSBatchError", "CacheError"] __all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE", "PHASE_MODE_TRACKING"] From 522ec60f6ee630f2b357a6cb53640b2161f3730c Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 12:28:07 +0100 Subject: [PATCH 03/13] hardware_testbench: don't allow unused *args --- artiq/test/hardware_testbench.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/test/hardware_testbench.py b/artiq/test/hardware_testbench.py index b8c1de6f8..1eaa6530d 100644 --- a/artiq/test/hardware_testbench.py +++ b/artiq/test/hardware_testbench.py @@ -109,7 +109,7 @@ class ExperimentCase(unittest.TestCase): # skip if ddb does not match requirements raise unittest.SkipTest(*e.args) - def execute(self, cls, *args, **kwargs): + def execute(self, cls, **kwargs): expid = { "file": sys.modules[cls.__module__].__file__, "class_name": cls.__name__, From 0bd9add95e4213a227e810922ba512e95d6c5d3a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 12:32:31 +0100 Subject: [PATCH 04/13] spi: fix frequency_to_div() --- artiq/coredevice/spi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 20095a8ff..80909e527 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -1,5 +1,5 @@ from artiq.language.core import (kernel, portable, seconds_to_mu, now_mu, - delay_mu, int) + delay_mu, int, mu_to_seconds) from artiq.language.units import MHz from artiq.coredevice.rtio import rtio_output, rtio_input_data @@ -58,7 +58,7 @@ class SPIMaster: @portable def frequency_to_div(self, f): - return int(1/(f*self.ref_period)) + 1 + return int(1/(f*mu_to_seconds(self.ref_period_mu))) + 1 @kernel def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz): From db5231216d2ba653103e634654e2668c1df7e19f Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 13:42:15 +0100 Subject: [PATCH 05/13] rtio: remove unused include --- artiq/runtime/rtio.h | 1 - 1 file changed, 1 deletion(-) diff --git a/artiq/runtime/rtio.h b/artiq/runtime/rtio.h index cdaf8a6e1..aad6460ba 100644 --- a/artiq/runtime/rtio.h +++ b/artiq/runtime/rtio.h @@ -2,7 +2,6 @@ #define __RTIO_H #include -#include "generated/csr.h" #define RTIO_O_STATUS_FULL 1 #define RTIO_O_STATUS_UNDERFLOW 2 From bf188d05bf3d2daf28a7c6551aa1624edf2d1d82 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 13:42:32 +0100 Subject: [PATCH 06/13] rtio: cleanup RTIOBusy message --- artiq/runtime/rtio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/artiq/runtime/rtio.c b/artiq/runtime/rtio.c index 385011cf5..92f02f651 100644 --- a/artiq/runtime/rtio.c +++ b/artiq/runtime/rtio.c @@ -42,8 +42,8 @@ static void rtio_process_exceptional_status( if(status & RTIO_O_STATUS_BUSY) { rtio_o_busy_reset_write(1); artiq_raise_from_c("RTIOBusy", - "RTIO busy at {0} mu, channel {1}", - timestamp, channel, 0); + "RTIO busy on channel {0}", + channel, 0, 0); } } From 58e0e670fc7d61cdaa6fea9d6434d2d047e441a7 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 15:39:16 +0100 Subject: [PATCH 07/13] tests: test spi business --- artiq/test/coredevice/test_spi.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 artiq/test/coredevice/test_spi.py diff --git a/artiq/test/coredevice/test_spi.py b/artiq/test/coredevice/test_spi.py new file mode 100644 index 000000000..ac1838d5d --- /dev/null +++ b/artiq/test/coredevice/test_spi.py @@ -0,0 +1,24 @@ +from artiq.experiment import * +from artiq.test.hardware_testbench import ExperimentCase + + +class Busy(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("spi0") + self.setattr_device("led") + + @kernel + def run(self): + self.spi0.set_config_mu() + delay(-8*ns) + self.spi0.set_config_mu() # causes the error + self.led.on() + self.led.sync() # registers the error + self.core.break_realtime() + self.spi0.set_config_mu() # raises the error + + +class SPITest(ExperimentCase): + def test_busy(self): + self.execute(Busy) From b50e3fabb03b54f98910e21e92cd0327b39af493 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 14:34:10 +0100 Subject: [PATCH 08/13] hardware_testbench: also print artiq_core_exeption --- artiq/test/hardware_testbench.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/artiq/test/hardware_testbench.py b/artiq/test/hardware_testbench.py index 1eaa6530d..a8726fce7 100644 --- a/artiq/test/hardware_testbench.py +++ b/artiq/test/hardware_testbench.py @@ -124,3 +124,6 @@ class ExperimentCase(unittest.TestCase): except CompileError as error: # Reduce amount of text on terminal. raise error from None + except Exception as exn: + exn.args = exn.args[0] + "\n" + str(exn.artiq_core_exception), + raise exn From 9a661bd27323e57ee88d6f563b5e2cc3429b7737 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 17:22:15 +0100 Subject: [PATCH 09/13] hardware_testbench: clean up artiq_core_exeption printing --- artiq/test/hardware_testbench.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/artiq/test/hardware_testbench.py b/artiq/test/hardware_testbench.py index a8726fce7..3cb0d3700 100644 --- a/artiq/test/hardware_testbench.py +++ b/artiq/test/hardware_testbench.py @@ -125,5 +125,7 @@ class ExperimentCase(unittest.TestCase): # Reduce amount of text on terminal. raise error from None except Exception as exn: - exn.args = exn.args[0] + "\n" + str(exn.artiq_core_exception), + if hasattr(exn, "artiq_core_exception"): + exn.args = "{}\n{}".format(exn.args[0], + exn.artiq_core_exception), raise exn From 8f6653ef72bb6a3839efef8c681e1be9191af1da Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 17:58:42 +0100 Subject: [PATCH 10/13] test_spi: simplify test, add collision vs busy test --- artiq/test/coredevice/test_spi.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/artiq/test/coredevice/test_spi.py b/artiq/test/coredevice/test_spi.py index ac1838d5d..436f720d9 100644 --- a/artiq/test/coredevice/test_spi.py +++ b/artiq/test/coredevice/test_spi.py @@ -2,6 +2,19 @@ from artiq.experiment import * from artiq.test.hardware_testbench import ExperimentCase +class Collision(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("spi0") + + @kernel + def run(self): + t = now_mu() + self.spi0.set_config_mu() + at_mu(t) + self.spi0.set_config_mu() + + class Busy(EnvExperiment): def build(self): self.setattr_device("core") @@ -10,8 +23,9 @@ class Busy(EnvExperiment): @kernel def run(self): + t = now_mu() self.spi0.set_config_mu() - delay(-8*ns) + 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 @@ -20,5 +34,10 @@ class Busy(EnvExperiment): class SPITest(ExperimentCase): + def test_collision(self): + with self.assertRaises(RTIOCollision): + self.execute(Collision) + def test_busy(self): - self.execute(Busy) + with self.assertRaises(RTIOBusy): + self.execute(Busy) From 10a09122eacce58c5c9add1af261c2d450f5bcc2 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 18:23:27 +0100 Subject: [PATCH 11/13] test_spi: break_realtime --- artiq/test/coredevice/test_spi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/artiq/test/coredevice/test_spi.py b/artiq/test/coredevice/test_spi.py index 436f720d9..53b1a3ec0 100644 --- a/artiq/test/coredevice/test_spi.py +++ b/artiq/test/coredevice/test_spi.py @@ -9,6 +9,7 @@ class Collision(EnvExperiment): @kernel def run(self): + self.core.break_realtime() t = now_mu() self.spi0.set_config_mu() at_mu(t) @@ -23,6 +24,7 @@ class Busy(EnvExperiment): @kernel def run(self): + self.core.break_realtime() t = now_mu() self.spi0.set_config_mu() at_mu(t + self.spi0.ref_period_mu) From 107e5cfbd4fa0e7fe16ef49b4b0b4e7b46c91454 Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 19:04:06 +0100 Subject: [PATCH 12/13] gateware/rtio: factor _BlindTransfer --- artiq/gateware/rtio/core.py | 54 ++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index 0ceccab4a..2fb9ee02b 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -54,6 +54,26 @@ class _RTIOCounter(Module): self.comb += gt.i.eq(self.value_rtio), self.value_sys.eq(gt.o) +class _BlindTransfer(Module): + def __init__(self): + self.i = Signal() + self.o = Signal() + + ps = PulseSynchronizer("rio", "rsys") + ps_ack = PulseSynchronizer("rsys", "rio") + self.submodules += ps, ps_ack + blind = Signal() + self.sync.rio += [ + If(self.i, blind.eq(1)), + If(ps_ack.o, blind.eq(0)) + ] + self.comb += [ + ps.i.eq(self.i & ~blind), + ps_ack.i.eq(ps.o), + self.o.eq(ps.o) + ] + + # CHOOSING A GUARD TIME # # The buffer must be transferred to the FIFO soon enough to account for: @@ -221,24 +241,17 @@ class _OutputManager(Module): self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack)) # FIFO read through buffer - # TODO: report error on stb & busy self.comb += [ dout_ack.eq( dout.timestamp[fine_ts_width:] == counter.value_rtio), interface.stb.eq(dout_stb & dout_ack) ] - busy_sync = PulseSynchronizer("rio", "rsys") - busy_ack_sync = PulseSynchronizer("rsys", "rio") - self.submodules += busy_sync, busy_ack_sync - busy_blind = Signal() - self.comb += busy_sync.i.eq(interface.stb & interface.busy & ~busy_blind) - self.sync.rio += [ - If(interface.stb & interface.busy, busy_blind.eq(1)), - If(busy_ack_sync.o, busy_blind.eq(0)) - ] + + busy_transfer = _BlindTransfer() + self.submodules += busy_transfer self.comb += [ - busy_ack_sync.i.eq(busy_sync.o), - self.busy.eq(busy_sync.o) + busy_transfer.i.eq(interface.stb & interface.busy), + self.busy.eq(busy_transfer.o), ] if data_width: @@ -263,7 +276,7 @@ class _InputManager(Module): self.readable = Signal() self.re = Signal() - + self.overflow = Signal() # pulsed # # # @@ -296,18 +309,11 @@ class _InputManager(Module): fifo.re.eq(self.re) ] - overflow_sync = PulseSynchronizer("rio", "rsys") - overflow_ack_sync = PulseSynchronizer("rsys", "rio") - self.submodules += overflow_sync, overflow_ack_sync - overflow_blind = Signal() - self.comb += overflow_sync.i.eq(fifo.we & ~fifo.writable & ~overflow_blind) - self.sync.rio += [ - If(fifo.we & ~fifo.writable, overflow_blind.eq(1)), - If(overflow_ack_sync.o, overflow_blind.eq(0)) - ] + overflow_transfer = _BlindTransfer() + self.submodules += overflow_transfer self.comb += [ - overflow_ack_sync.i.eq(overflow_sync.o), - self.overflow.eq(overflow_sync.o) + overflow_transfer.i.eq(fifo.we & ~fifo.writable), + self.overflow.eq(overflow_transfer.o), ] From 9edaf16735552c5e5618f7c2e3633323f96d018b Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Wed, 9 Mar 2016 22:11:32 +0100 Subject: [PATCH 13/13] exceptions: clarify RTIOBusy --- artiq/coredevice/exceptions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/artiq/coredevice/exceptions.py b/artiq/coredevice/exceptions.py index 987f5a127..6317edfe6 100644 --- a/artiq/coredevice/exceptions.py +++ b/artiq/coredevice/exceptions.py @@ -99,15 +99,14 @@ class RTIOCollision(Exception): artiq_builtin = True class RTIOBusy(Exception): - """Raised when an event could not be executed because the given channel - was already busy executing a previous event. The two events were not - sufficiently spaced on the timeline. + """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 after the error condition appeared. More + 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 is discarded and the RTIO core keeps operating. + The offending event was discarded. """ artiq_builtin = True