diff --git a/artiq/firmware/satman/lib.rs b/artiq/firmware/satman/lib.rs index 409ffdcd5..9de4c53f4 100644 --- a/artiq/firmware/satman/lib.rs +++ b/artiq/firmware/satman/lib.rs @@ -108,6 +108,9 @@ fn process_errors() { if errors & 8 != 0 { error!("write overflow"); } + if errors & 16 != 0 { + error!("write sequence error"); + } } diff --git a/artiq/gateware/drtio/rt_errors_satellite.py b/artiq/gateware/drtio/rt_errors_satellite.py index 3a67e88d8..e93a7bc9c 100644 --- a/artiq/gateware/drtio/rt_errors_satellite.py +++ b/artiq/gateware/drtio/rt_errors_satellite.py @@ -8,7 +8,7 @@ from artiq.gateware.rtio.cdc import BlindTransfer class RTErrorsSatellite(Module, AutoCSR): def __init__(self, rt_packet, ios): - self.protocol_error = CSR(4) + self.protocol_error = CSR(5) self.rtio_error = CSR(2) def error_csr(csr, *sources): @@ -23,14 +23,15 @@ class RTErrorsSatellite(Module, AutoCSR): ] self.comb += csr.w[n].eq(pending) - # The master is normally responsible for avoiding output overflows and - # output underflows. + # The master is normally responsible for avoiding output overflows, + # output underflows, and sequence errors. # Error reports here are only for diagnosing internal ARTIQ bugs. error_csr(self.protocol_error, rt_packet.unknown_packet_type, rt_packet.packet_truncated, ios.write_underflow, - ios.write_overflow) + ios.write_overflow, + ios.write_sequence_error) error_csr(self.rtio_error, ios.collision, ios.busy) diff --git a/artiq/gateware/drtio/rt_ios_satellite.py b/artiq/gateware/drtio/rt_ios_satellite.py index 74e7cb80c..61ba3791b 100644 --- a/artiq/gateware/drtio/rt_ios_satellite.py +++ b/artiq/gateware/drtio/rt_ios_satellite.py @@ -11,6 +11,7 @@ class IOS(Module): def __init__(self, rt_packet, channels, max_fine_ts_width, full_ts_width): self.write_underflow = Signal() self.write_overflow = Signal() + self.write_sequence_error = Signal() self.collision = Signal() self.busy = Signal() @@ -46,6 +47,10 @@ class IOS(Module): fine_ts_width = rtlink.get_fine_ts_width(interface) assert fine_ts_width <= max_fine_ts_width + we = Signal() + self.comb += we.eq(rt_packet.write_stb + & (rt_packet.write_channel == n)) + # latency compensation if interface.delay: tsc_comp = Signal.like(self.tsc) @@ -71,30 +76,86 @@ class IOS(Module): fifo_out.raw_bits().eq(fifo.dout) ] + # Buffer + buf_pending = Signal() + buf = Record(ev_layout) + buf_just_written = Signal() + + # Special cases + replace = Signal() + sequence_error = Signal() + collision = Signal() + any_error = Signal() + if interface.enable_replace: + # Note: replace may be asserted at the same time as collision + # when addresses are different. In that case, it is a collision. + self.sync.rio += replace.eq(rt_packet.write_timestamp == buf.timestamp) + # Detect sequence errors on coarse timestamps only + # so that they are mutually exclusive with collision errors. + self.sync.rio += sequence_error.eq(rt_packet.write_timestamp[fine_ts_width:] < + buf.timestamp[fine_ts_width:]) + if interface.enable_replace: + if address_width: + different_addresses = rt_packet.write_address != buf.address + else: + different_addresses = 0 + if fine_ts_width: + self.sync.rio += collision.eq( + (rt_packet.write_timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) + & ((rt_packet.write_timestamp[:fine_ts_width] != buf.timestamp[:fine_ts_width]) + |different_addresses)) + else: + self.sync.rio += collision.eq( + rt_packet.write_timestamp[fine_ts_width:] == buf.timestamp[fine_ts_width:]) + self.comb += any_error.eq(sequence_error | collision) + self.sync.rio += [ + If(we & sequence_error, self.write_sequence_error.eq(1)), + If(we & collision, self.collision.eq(1)) + ] + + # Buffer read and FIFO write + self.comb += fifo_in.eq(buf) + in_guard_time = Signal() + self.comb += in_guard_time.eq( + buf.timestamp[fine_ts_width:] < tsc_comp + 4) + self.sync.rio += If(in_guard_time, buf_pending.eq(0)) + report_underflow = Signal() + self.comb += \ + If(buf_pending, + If(in_guard_time, + If(buf_just_written, + report_underflow.eq(1) + ).Else( + fifo.we.eq(1) + ) + ), + If(we & ~replace & ~any_error, + fifo.we.eq(1) + ) + ) + self.sync.rio += If(report_underflow, self.write_underflow.eq(1)) + + # Buffer write + # Must come after read to handle concurrent read+write properly + self.sync.rio += [ + buf_just_written.eq(0), + If(we & ~any_error, + buf_just_written.eq(1), + buf_pending.eq(1), + buf.timestamp.eq( + rt_packet.write_timestamp[max_fine_ts_width-fine_ts_width:]), + buf.data.eq(rt_packet.write_data) if data_width else [], + buf.address.eq(rt_packet.write_address) if address_width else [], + ), + If(we & ~fifo.writable, self.write_overflow.eq(1)) + ] + # FIFO level self.sync.rio += \ If(rt_packet.fifo_space_update & (rt_packet.fifo_space_channel == n), rt_packet.fifo_space.eq(channel.ofifo_depth - fifo.level)) - # FIFO write - self.comb += fifo.we.eq(rt_packet.write_stb - & (rt_packet.write_channel == n)) - self.sync.rio += [ - If(fifo.we, - If(rt_packet.write_timestamp[max_fine_ts_width:] < (tsc_comp + 4), - self.write_underflow.eq(1) - ), - If(~fifo.writable, self.write_overflow.eq(1)) - ) - ] - if data_width: - self.comb += fifo_in.data.eq(rt_packet.write_data) - if address_width: - self.comb += fifo_in.address.eq(rt_packet.write_address) - self.comb += fifo_in.timestamp.eq( - rt_packet.write_timestamp[max_fine_ts_width-fine_ts_width:]) - # FIFO read self.sync.rio += [ fifo.re.eq(0), diff --git a/artiq/gateware/drtio/rt_packet_satellite.py b/artiq/gateware/drtio/rt_packet_satellite.py index c490ca34b..13bc921bb 100644 --- a/artiq/gateware/drtio/rt_packet_satellite.py +++ b/artiq/gateware/drtio/rt_packet_satellite.py @@ -22,6 +22,8 @@ class RTPacketSatellite(Module): self.fifo_space_update = Signal() self.fifo_space = Signal(16) + # write parameters are stable one cycle before stb is asserted, + # and when stb is asserted. self.write_stb = Signal() self.write_timestamp = Signal(64) self.write_channel = Signal(16)