forked from M-Labs/artiq
1
0
Fork 0

Merge branch 'master' of github.com:m-labs/artiq

This commit is contained in:
Sebastien Bourdeauducq 2016-03-10 11:15:30 +08:00
commit f4f95d330b
9 changed files with 120 additions and 25 deletions

View File

@ -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"]

View File

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

View File

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

View File

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

View File

@ -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,

View File

@ -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);
}
} }

View File

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

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 # 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