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 import exceptions, dds, spi
|
||||||
from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError,
|
from artiq.coredevice.exceptions import (RTIOUnderflow, RTIOSequenceError,
|
||||||
RTIOCollision, RTIOOverflow,
|
RTIOCollision, RTIOOverflow, RTIOBusy,
|
||||||
DDSBatchError, CacheError)
|
DDSBatchError, CacheError)
|
||||||
from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE,
|
from artiq.coredevice.dds import (PHASE_MODE_CONTINUOUS, PHASE_MODE_ABSOLUTE,
|
||||||
PHASE_MODE_TRACKING)
|
PHASE_MODE_TRACKING)
|
||||||
|
|
||||||
__all__ = []
|
__all__ = []
|
||||||
__all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollision",
|
__all__ += ["RTIOUnderflow", "RTIOSequenceError", "RTIOCollision",
|
||||||
"RTIOOverflow", "DDSBatchError", "CacheError"]
|
"RTIOOverflow", "RTIOBusy", "DDSBatchError", "CacheError"]
|
||||||
__all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE",
|
__all__ += ["PHASE_MODE_CONTINUOUS", "PHASE_MODE_ABSOLUTE",
|
||||||
"PHASE_MODE_TRACKING"]
|
"PHASE_MODE_TRACKING"]
|
||||||
|
|
|
@ -82,7 +82,7 @@ class _DDSGeneric:
|
||||||
:param bus: name of the DDS bus device that this DDS is connected to.
|
:param bus: name of the DDS bus device that this DDS is connected to.
|
||||||
:param channel: channel number of the DDS device to control.
|
: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_dds = dmgr.get(core_dds_device)
|
||||||
self.core = self.core_dds.core
|
self.core = self.core_dds.core
|
||||||
self.bus_channel = bus_channel
|
self.bus_channel = bus_channel
|
||||||
|
|
|
@ -98,6 +98,18 @@ class RTIOCollision(Exception):
|
||||||
"""
|
"""
|
||||||
artiq_builtin = True
|
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):
|
class RTIOOverflow(Exception):
|
||||||
"""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
|
||||||
input FIFO because it was full (CPU not reading fast enough).
|
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,
|
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.language.units import MHz
|
||||||
from artiq.coredevice.rtio import rtio_output, rtio_input_data
|
from artiq.coredevice.rtio import rtio_output, rtio_input_data
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class SPIMaster:
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def frequency_to_div(self, f):
|
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
|
@kernel
|
||||||
def set_config(self, flags=0, write_freq=20*MHz, read_freq=20*MHz):
|
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)
|
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
|
# CHOOSING A GUARD TIME
|
||||||
#
|
#
|
||||||
# The buffer must be transferred to the FIFO soon enough to account for:
|
# 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.underflow = Signal() # valid 1 cycle after we, pulsed
|
||||||
self.sequence_error = Signal()
|
self.sequence_error = Signal()
|
||||||
self.collision = 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
|
# Note: replace may be asserted at the same time as collision
|
||||||
# when addresses are different. In that case, it is a 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 += replace.eq(self.ev.timestamp == buf.timestamp)
|
||||||
self.sync.rsys += \
|
|
||||||
# Detect sequence errors on coarse timestamps only
|
# Detect sequence errors on coarse timestamps only
|
||||||
# so that they are mutually exclusive with collision errors.
|
# so that they are mutually exclusive with collision errors.
|
||||||
sequence_error.eq(self.ev.timestamp[fine_ts_width:]
|
self.sync.rsys += sequence_error.eq(self.ev.timestamp[fine_ts_width:] <
|
||||||
< buf.timestamp[fine_ts_width:])
|
buf.timestamp[fine_ts_width:])
|
||||||
if interface.enable_replace:
|
if interface.enable_replace:
|
||||||
if hasattr(self.ev, "a"):
|
if hasattr(self.ev, "a"):
|
||||||
different_addresses = self.ev.a != buf.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))
|
self.comb += fifo.re.eq(fifo.readable & (~dout_stb | dout_ack))
|
||||||
|
|
||||||
# FIFO read through buffer
|
# FIFO read through buffer
|
||||||
# TODO: report error on stb & busy
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
dout_ack.eq(
|
dout_ack.eq(
|
||||||
dout.timestamp[fine_ts_width:] == counter.value_rtio),
|
dout.timestamp[fine_ts_width:] == counter.value_rtio),
|
||||||
interface.stb.eq(dout_stb & dout_ack)
|
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:
|
if data_width:
|
||||||
self.comb += interface.data.eq(dout.data)
|
self.comb += interface.data.eq(dout.data)
|
||||||
if address_width:
|
if address_width:
|
||||||
|
@ -264,18 +291,11 @@ class _InputManager(Module):
|
||||||
fifo.re.eq(self.re)
|
fifo.re.eq(self.re)
|
||||||
]
|
]
|
||||||
|
|
||||||
overflow_sync = PulseSynchronizer("rio", "rsys")
|
overflow_transfer = _BlindTransfer()
|
||||||
overflow_ack_sync = PulseSynchronizer("rsys", "rio")
|
self.submodules += overflow_transfer
|
||||||
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))
|
|
||||||
]
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
overflow_ack_sync.i.eq(overflow_sync.o),
|
overflow_transfer.i.eq(fifo.we & ~fifo.writable),
|
||||||
self.overflow.eq(overflow_sync.o)
|
self.overflow.eq(overflow_transfer.o),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -321,10 +341,11 @@ 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(4)
|
self.o_status = CSRStatus(5)
|
||||||
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_reset = CSR()
|
self.o_collision_reset = CSR()
|
||||||
|
self.o_busy_reset = CSR()
|
||||||
|
|
||||||
if data_width:
|
if data_width:
|
||||||
self.i_data = CSRStatus(data_width)
|
self.i_data = CSRStatus(data_width)
|
||||||
|
@ -412,6 +433,7 @@ class RTIO(Module):
|
||||||
underflow = Signal()
|
underflow = Signal()
|
||||||
sequence_error = Signal()
|
sequence_error = Signal()
|
||||||
collision = Signal()
|
collision = Signal()
|
||||||
|
busy = 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)),
|
||||||
|
@ -419,14 +441,18 @@ class RTIO(Module):
|
||||||
sequence_error.eq(0)),
|
sequence_error.eq(0)),
|
||||||
If(selected & self.kcsrs.o_collision_reset.re,
|
If(selected & self.kcsrs.o_collision_reset.re,
|
||||||
collision.eq(0)),
|
collision.eq(0)),
|
||||||
|
If(selected & self.kcsrs.o_busy_reset.re,
|
||||||
|
busy.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, collision.eq(1))
|
If(o_manager.collision, collision.eq(1)),
|
||||||
|
If(o_manager.busy, busy.eq(1))
|
||||||
]
|
]
|
||||||
o_statuses.append(Cat(~o_manager.writable,
|
o_statuses.append(Cat(~o_manager.writable,
|
||||||
underflow,
|
underflow,
|
||||||
sequence_error,
|
sequence_error,
|
||||||
collision))
|
collision,
|
||||||
|
busy))
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
@ -39,6 +39,12 @@ static void rtio_process_exceptional_status(
|
||||||
"RTIO collision at {0} mu, channel {1}",
|
"RTIO collision at {0} mu, channel {1}",
|
||||||
timestamp, channel, 0);
|
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_UNDERFLOW 2
|
||||||
#define RTIO_O_STATUS_SEQUENCE_ERROR 4
|
#define RTIO_O_STATUS_SEQUENCE_ERROR 4
|
||||||
#define RTIO_O_STATUS_COLLISION 8
|
#define RTIO_O_STATUS_COLLISION 8
|
||||||
|
#define RTIO_O_STATUS_BUSY 16
|
||||||
#define RTIO_I_STATUS_EMPTY 1
|
#define RTIO_I_STATUS_EMPTY 1
|
||||||
#define RTIO_I_STATUS_OVERFLOW 2
|
#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
|
# skip if ddb does not match requirements
|
||||||
raise unittest.SkipTest(*e.args)
|
raise unittest.SkipTest(*e.args)
|
||||||
|
|
||||||
def execute(self, cls, *args, **kwargs):
|
def execute(self, cls, **kwargs):
|
||||||
expid = {
|
expid = {
|
||||||
"file": sys.modules[cls.__module__].__file__,
|
"file": sys.modules[cls.__module__].__file__,
|
||||||
"class_name": cls.__name__,
|
"class_name": cls.__name__,
|
||||||
|
@ -124,3 +124,8 @@ class ExperimentCase(unittest.TestCase):
|
||||||
except CompileError as error:
|
except CompileError as error:
|
||||||
# Reduce amount of text on terminal.
|
# Reduce amount of text on terminal.
|
||||||
raise error from None
|
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