forked from M-Labs/artiq
Merge branch 'master' of github.com:m-labs/artiq
This commit is contained in:
commit
f4f95d330b
|
@ -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"]
|
||||
|
|
|
@ -82,7 +82,7 @@ class _DDSGeneric:
|
|||
:param bus: name of the DDS bus device that this DDS is connected to.
|
||||
:param channel: channel number of the DDS device to control.
|
||||
"""
|
||||
def __init__(self, dmgr, sysclk, bus_channel, channel, core_dds_device="core_dds"):
|
||||
def __init__(self, dmgr, bus_channel, channel, core_dds_device="core_dds"):
|
||||
self.core_dds = dmgr.get(core_dds_device)
|
||||
self.core = self.core_dds.core
|
||||
self.bus_channel = bus_channel
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
# # #
|
||||
|
||||
|
@ -132,11 +153,10 @@ class _OutputManager(Module):
|
|||
# Note: replace may be asserted at the same time as collision
|
||||
# when addresses are different. In that case, it is a collision.
|
||||
self.sync.rsys += replace.eq(self.ev.timestamp == buf.timestamp)
|
||||
self.sync.rsys += \
|
||||
# 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:])
|
||||
self.sync.rsys += sequence_error.eq(self.ev.timestamp[fine_ts_width:] <
|
||||
buf.timestamp[fine_ts_width:])
|
||||
if interface.enable_replace:
|
||||
if hasattr(self.ev, "a"):
|
||||
different_addresses = self.ev.a != buf.a
|
||||
|
@ -203,12 +223,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:
|
||||
|
@ -264,18 +291,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),
|
||||
]
|
||||
|
||||
|
||||
|
@ -321,10 +341,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)
|
||||
|
@ -412,6 +433,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)),
|
||||
|
@ -419,14 +441,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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue