Merge branch 'rtiobusy' (closes #308)

* rtiobusy:
  exceptions: clarify RTIOBusy
  gateware/rtio: factor _BlindTransfer
  test_spi: break_realtime
  test_spi: simplify test, add collision vs busy test
  hardware_testbench: clean up artiq_core_exeption printing
  hardware_testbench: also print artiq_core_exeption
  tests: test spi business
  rtio: cleanup RTIOBusy message
  rtio: remove unused include
  spi: fix frequency_to_div()
  hardware_testbench: don't allow unused *args
  coredevice: add RTIOBusy to __all__
  rtio: add RTIOBusy
This commit is contained in:
Robert Jördens 2016-03-09 22:12:24 +01:00
commit cb6c28006c
8 changed files with 117 additions and 21 deletions

View File

@ -1,12 +1,12 @@
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)
__all__ = []
__all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollision",
"RTIOOverflow", "DDSBatchError", "CacheError"]
"RTIOOverflow", "RTIOBusy", "DDSBatchError", "CacheError"]
__all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE",
"PHASE_MODE_TRACKING"]

View File

@ -98,6 +98,18 @@ class RTIOCollision(Exception):
"""
artiq_builtin = True
class RTIOBusy(Exception):
"""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 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 was discarded.
"""
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).

View File

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

View File

@ -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:
@ -104,6 +124,7 @@ class _OutputManager(Module):
self.underflow = Signal() # valid 1 cycle after we, pulsed
self.sequence_error = Signal()
self.collision = Signal()
self.busy = Signal() # pulsed
# # #
@ -220,12 +241,19 @@ 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_transfer = _BlindTransfer()
self.submodules += busy_transfer
self.comb += [
busy_transfer.i.eq(interface.stb & interface.busy),
self.busy.eq(busy_transfer.o),
]
if data_width:
self.comb += interface.data.eq(dout.data)
if address_width:
@ -248,7 +276,7 @@ class _InputManager(Module):
self.readable = Signal()
self.re = Signal()
self.overflow = Signal() # pulsed
# # #
@ -281,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),
]
@ -339,10 +360,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)
@ -430,6 +452,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)),
@ -437,14 +460,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,

View File

@ -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 on channel {0}",
channel, 0, 0);
}
}

View File

@ -7,6 +7,7 @@
#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

View File

@ -0,0 +1,45 @@
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):
self.core.break_realtime()
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")
self.setattr_device("spi0")
self.setattr_device("led")
@kernel
def run(self):
self.core.break_realtime()
t = now_mu()
self.spi0.set_config_mu()
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
self.core.break_realtime()
self.spi0.set_config_mu() # raises the error
class SPITest(ExperimentCase):
def test_collision(self):
with self.assertRaises(RTIOCollision):
self.execute(Collision)
def test_busy(self):
with self.assertRaises(RTIOBusy):
self.execute(Busy)

View File

@ -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__,
@ -124,3 +124,8 @@ class ExperimentCase(unittest.TestCase):
except CompileError as error:
# Reduce amount of text on terminal.
raise error from None
except Exception as exn:
if hasattr(exn, "artiq_core_exception"):
exn.args = "{}\n{}".format(exn.args[0],
exn.artiq_core_exception),
raise exn