forked from M-Labs/artiq
285 lines
9.8 KiB
Python
285 lines
9.8 KiB
Python
"""Real-time controller for master"""
|
|
|
|
from migen import *
|
|
from migen.genlib.cdc import MultiReg
|
|
from migen.genlib.misc import WaitTimer
|
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
|
|
|
from misoc.interconnect.csr import *
|
|
|
|
from artiq.gateware.rtio.cdc import GrayCodeTransfer
|
|
from artiq.gateware.rtio import cri
|
|
|
|
|
|
class _CSRs(AutoCSR):
|
|
def __init__(self):
|
|
self.protocol_error = CSR(3)
|
|
|
|
self.tsc_correction = CSRStorage(64)
|
|
self.set_time = CSR()
|
|
self.underflow_margin = CSRStorage(16, reset=200)
|
|
|
|
self.reset = CSR()
|
|
self.reset_phy = CSR()
|
|
|
|
self.o_get_buffer_space = CSR()
|
|
self.o_dbg_buffer_space = CSRStatus(16)
|
|
self.o_dbg_buffer_space_req_cnt = CSRStatus(32)
|
|
self.o_wait = CSRStatus()
|
|
|
|
|
|
class RTIOCounter(Module):
|
|
def __init__(self, width):
|
|
self.width = width
|
|
# Timestamp counter in RTIO domain
|
|
self.value_rtio = Signal(width)
|
|
# Timestamp counter resynchronized to sys domain
|
|
# Lags behind value_rtio, monotonic and glitch-free
|
|
self.value_sys = Signal(width)
|
|
|
|
# # #
|
|
|
|
# note: counter is in rtio domain and never affected by the reset CSRs
|
|
self.sync.rtio += self.value_rtio.eq(self.value_rtio + 1)
|
|
gt = GrayCodeTransfer(width)
|
|
self.submodules += gt
|
|
self.comb += gt.i.eq(self.value_rtio), self.value_sys.eq(gt.o)
|
|
|
|
|
|
class RTController(Module):
|
|
def __init__(self, rt_packet, channel_count, fine_ts_width):
|
|
self.csrs = _CSRs()
|
|
self.cri = cri.Interface()
|
|
|
|
# protocol errors
|
|
err_unknown_packet_type = Signal()
|
|
err_packet_truncated = Signal()
|
|
signal_buffer_space_timeout = Signal()
|
|
err_buffer_space_timeout = Signal()
|
|
self.sync.sys_with_rst += [
|
|
If(self.csrs.protocol_error.re,
|
|
If(self.csrs.protocol_error.r[0], err_unknown_packet_type.eq(0)),
|
|
If(self.csrs.protocol_error.r[1], err_packet_truncated.eq(0)),
|
|
If(self.csrs.protocol_error.r[2], err_buffer_space_timeout.eq(0))
|
|
),
|
|
If(rt_packet.err_unknown_packet_type, err_unknown_packet_type.eq(1)),
|
|
If(rt_packet.err_packet_truncated, err_packet_truncated.eq(1)),
|
|
If(signal_buffer_space_timeout, err_buffer_space_timeout.eq(1))
|
|
]
|
|
self.comb += self.csrs.protocol_error.w.eq(
|
|
Cat(err_unknown_packet_type, err_packet_truncated, err_buffer_space_timeout))
|
|
|
|
# master RTIO counter and counter synchronization
|
|
self.submodules.counter = RTIOCounter(64-fine_ts_width)
|
|
self.comb += self.cri.counter.eq(self.counter.value_sys << fine_ts_width)
|
|
tsc_correction = Signal(64)
|
|
self.csrs.tsc_correction.storage.attr.add("no_retiming")
|
|
self.specials += MultiReg(self.csrs.tsc_correction.storage, tsc_correction)
|
|
self.comb += [
|
|
rt_packet.tsc_value.eq(
|
|
self.counter.value_rtio + tsc_correction),
|
|
self.csrs.set_time.w.eq(rt_packet.set_time_stb)
|
|
]
|
|
self.sync += [
|
|
If(rt_packet.set_time_ack, rt_packet.set_time_stb.eq(0)),
|
|
If(self.csrs.set_time.re, rt_packet.set_time_stb.eq(1))
|
|
]
|
|
|
|
# reset
|
|
self.sync += [
|
|
If(rt_packet.reset_ack, rt_packet.reset_stb.eq(0)),
|
|
If(self.csrs.reset.re,
|
|
rt_packet.reset_stb.eq(1),
|
|
rt_packet.reset_phy.eq(0)
|
|
),
|
|
If(self.csrs.reset_phy.re,
|
|
rt_packet.reset_stb.eq(1),
|
|
rt_packet.reset_phy.eq(1)
|
|
),
|
|
]
|
|
|
|
local_reset = Signal(reset=1)
|
|
self.sync += local_reset.eq(self.csrs.reset.re)
|
|
local_reset.attr.add("no_retiming")
|
|
self.clock_domains.cd_sys_with_rst = ClockDomain()
|
|
self.clock_domains.cd_rtio_with_rst = ClockDomain()
|
|
self.comb += [
|
|
self.cd_sys_with_rst.clk.eq(ClockSignal()),
|
|
self.cd_sys_with_rst.rst.eq(local_reset)
|
|
]
|
|
self.comb += self.cd_rtio_with_rst.clk.eq(ClockSignal("rtio"))
|
|
self.specials += AsyncResetSynchronizer(self.cd_rtio_with_rst, local_reset)
|
|
|
|
# common packet fields
|
|
chan_sel = self.cri.chan_sel[:16]
|
|
rt_packet_buffer_request = Signal()
|
|
rt_packet_read_request = Signal()
|
|
self.comb += [
|
|
rt_packet.sr_channel.eq(chan_sel),
|
|
rt_packet.sr_address.eq(self.cri.o_address),
|
|
rt_packet.sr_data.eq(self.cri.o_data),
|
|
rt_packet.sr_timestamp.eq(self.cri.timestamp),
|
|
If(rt_packet_buffer_request,
|
|
rt_packet.sr_notwrite.eq(1),
|
|
rt_packet.sr_address.eq(0)
|
|
),
|
|
If(rt_packet_read_request,
|
|
rt_packet.sr_notwrite.eq(1),
|
|
rt_packet.sr_address.eq(1)
|
|
)
|
|
]
|
|
|
|
# output status
|
|
o_status_wait = Signal()
|
|
o_status_underflow = Signal()
|
|
self.comb += [
|
|
self.cri.o_status.eq(Cat(
|
|
o_status_wait, o_status_underflow)),
|
|
self.csrs.o_wait.status.eq(o_status_wait)
|
|
]
|
|
o_underflow_set = Signal()
|
|
self.sync.sys_with_rst += [
|
|
If(self.cri.cmd == cri.commands["write"],
|
|
o_status_underflow.eq(0)
|
|
),
|
|
If(o_underflow_set, o_status_underflow.eq(1))
|
|
]
|
|
|
|
timeout_counter = WaitTimer(8191)
|
|
self.submodules += timeout_counter
|
|
|
|
cond_underflow = Signal()
|
|
self.comb += cond_underflow.eq((self.cri.timestamp[fine_ts_width:]
|
|
- self.csrs.underflow_margin.storage[fine_ts_width:]) < self.counter.value_sys)
|
|
|
|
buffer_space = Signal(16)
|
|
|
|
# input status
|
|
i_status_wait_event = Signal()
|
|
i_status_overflow = Signal()
|
|
i_status_wait_status = Signal()
|
|
self.comb += self.cri.i_status.eq(Cat(
|
|
i_status_wait_event, i_status_overflow, i_status_wait_status))
|
|
|
|
load_read_reply = Signal()
|
|
self.sync.sys_with_rst += [
|
|
If(load_read_reply,
|
|
i_status_wait_event.eq(0),
|
|
i_status_overflow.eq(0),
|
|
If(rt_packet.read_no_event,
|
|
If(rt_packet.read_is_overflow,
|
|
i_status_overflow.eq(1)
|
|
).Else(
|
|
i_status_wait_event.eq(1)
|
|
)
|
|
),
|
|
self.cri.i_data.eq(rt_packet.read_data),
|
|
self.cri.i_timestamp.eq(rt_packet.read_timestamp)
|
|
)
|
|
]
|
|
|
|
# FSM
|
|
fsm = ClockDomainsRenamer("sys_with_rst")(FSM())
|
|
self.submodules += fsm
|
|
|
|
fsm.act("IDLE",
|
|
If(self.cri.cmd == cri.commands["write"],
|
|
If(cond_underflow,
|
|
o_underflow_set.eq(1)
|
|
).Else(
|
|
NextState("WRITE")
|
|
)
|
|
),
|
|
If(self.cri.cmd == cri.commands["read"], NextState("READ")),
|
|
If(self.csrs.o_get_buffer_space.re, NextState("GET_BUFFER_SPACE"))
|
|
)
|
|
fsm.act("WRITE",
|
|
o_status_wait.eq(1),
|
|
rt_packet.sr_stb.eq(1),
|
|
If(rt_packet.sr_ack,
|
|
NextValue(buffer_space, buffer_space - 1),
|
|
If(buffer_space <= 1,
|
|
NextState("GET_BUFFER_SPACE")
|
|
).Else(
|
|
NextState("IDLE")
|
|
)
|
|
)
|
|
)
|
|
fsm.act("GET_BUFFER_SPACE",
|
|
o_status_wait.eq(1),
|
|
rt_packet.buffer_space_not_ack.eq(1),
|
|
rt_packet_buffer_request.eq(1),
|
|
rt_packet.sr_stb.eq(1),
|
|
If(rt_packet.sr_ack,
|
|
NextState("GET_BUFFER_SPACE_REPLY")
|
|
)
|
|
)
|
|
fsm.act("GET_BUFFER_SPACE_REPLY",
|
|
o_status_wait.eq(1),
|
|
NextValue(buffer_space, rt_packet.buffer_space),
|
|
rt_packet.buffer_space_not_ack.eq(1),
|
|
If(rt_packet.buffer_space_not,
|
|
If(rt_packet.buffer_space != 0,
|
|
NextState("IDLE")
|
|
).Else(
|
|
NextState("GET_BUFFER_SPACE")
|
|
)
|
|
),
|
|
timeout_counter.wait.eq(1),
|
|
If(timeout_counter.done,
|
|
signal_buffer_space_timeout.eq(1),
|
|
NextState("GET_BUFFER_SPACE")
|
|
)
|
|
)
|
|
fsm.act("READ",
|
|
i_status_wait_status.eq(1),
|
|
rt_packet.read_not_ack.eq(1),
|
|
rt_packet_read_request.eq(1),
|
|
rt_packet.sr_stb.eq(1),
|
|
If(rt_packet.sr_ack,
|
|
NextState("GET_READ_REPLY")
|
|
)
|
|
)
|
|
fsm.act("GET_READ_REPLY",
|
|
i_status_wait_status.eq(1),
|
|
rt_packet.read_not_ack.eq(1),
|
|
If(rt_packet.read_not,
|
|
load_read_reply.eq(1),
|
|
NextState("IDLE")
|
|
)
|
|
)
|
|
|
|
# debug CSRs
|
|
self.comb += self.csrs.o_dbg_buffer_space.status.eq(buffer_space),
|
|
self.sync += \
|
|
If((rt_packet.sr_stb & rt_packet.sr_ack & rt_packet_buffer_request),
|
|
self.csrs.o_dbg_buffer_space_req_cnt.status.eq(
|
|
self.csrs.o_dbg_buffer_space_req_cnt.status + 1)
|
|
)
|
|
|
|
def get_csrs(self):
|
|
return self.csrs.get_csrs()
|
|
|
|
|
|
class RTManager(Module, AutoCSR):
|
|
def __init__(self, rt_packet):
|
|
self.request_echo = CSR()
|
|
|
|
self.update_packet_cnt = CSR()
|
|
self.packet_cnt_tx = CSRStatus(32)
|
|
self.packet_cnt_rx = CSRStatus(32)
|
|
|
|
# # #
|
|
|
|
self.comb += self.request_echo.w.eq(rt_packet.echo_stb)
|
|
self.sync += [
|
|
If(rt_packet.echo_ack, rt_packet.echo_stb.eq(0)),
|
|
If(self.request_echo.re, rt_packet.echo_stb.eq(1))
|
|
]
|
|
|
|
self.sync += \
|
|
If(self.update_packet_cnt.re,
|
|
self.packet_cnt_tx.status.eq(rt_packet.packet_cnt_tx),
|
|
self.packet_cnt_rx.status.eq(rt_packet.packet_cnt_rx)
|
|
)
|